Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/inline/inlineChatNotebookGeneratePrompt.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 { ConfigKey, IConfigurationService } from '../../../../platform/configuration/common/configurationService';
9
import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';
10
import { INotebookService } from '../../../../platform/notebook/common/notebookService';
11
import { IParserService } from '../../../../platform/parser/node/parserService';
12
import { ITabsAndEditorsService } from '../../../../platform/tabs/common/tabsAndEditorsService';
13
import { IExperimentationService } from '../../../../platform/telemetry/common/nullExperimentationService';
14
import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService';
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 { IDocumentContext } from '../../../prompt/node/documentContext';
19
import { EarlyStopping, LeadingMarkdownStreaming, ReplyInterpreterMetaData } from '../../../prompt/node/intents';
20
import { TextPieceClassifiers } from '../../../prompt/node/streamingEdits';
21
import { InstructionMessage } from '../base/instructionMessage';
22
import { LegacySafetyRules } from '../base/safetyRules';
23
import { JupyterNotebookRules } from '../notebook/commonPrompts';
24
import { ChatToolReferences, ChatVariables, UserQuery } from '../panel/chatVariables';
25
import { HistoryWithInstructions } from '../panel/conversationHistory';
26
import { CustomInstructions } from '../panel/customInstructions';
27
import { CodeBlock } from '../panel/safeElements';
28
import { InlineChatEditCodePromptProps } from './inlineChatEditCodePrompt';
29
import { promptPriorities } from './inlineChatNotebookCommon';
30
import { generateSelectionContextInNotebook, InlineChatCustomNotebookCellsContextRenderer, InlineChatCustomNotebookInfoRenderer, InlineChatJupyterNotebookCellsContextRenderer, InlineChatJupyterNotebookCellsContextTagBasedRenderer, InlineChatNotebookBasePromptState, InlineChatNotebookSelectionCommonProps, InlineChatNotebookSelectionState, InlineChatNotebookVariables } from './inlineChatNotebookCommonPromptElements';
31
import { createPromptingSummarizedDocument } from './promptingSummarizedDocument';
32
33
export class InlineChatNotebookGeneratePrompt extends PromptElement<InlineChatEditCodePromptProps, InlineChatNotebookBasePromptState> {
34
constructor(
35
props: InlineChatEditCodePromptProps,
36
@IIgnoreService private readonly ignoreService: IIgnoreService,
37
@IParserService private readonly parserService: IParserService,
38
@IExperimentationService private readonly experimentationService: IExperimentationService
39
) {
40
super(props);
41
}
42
43
override async prepare(sizing: PromptSizing): Promise<InlineChatNotebookBasePromptState> {
44
const { documentContext: context } = this.props;
45
const isIgnored = await this.ignoreService.isCopilotIgnored(context.document.uri);
46
const wholeRange = context.document.validateRange(context.wholeRange);
47
const summarizedDocument = await createPromptingSummarizedDocument(
48
this.parserService,
49
context.document,
50
context.fileIndentInfo,
51
wholeRange,
52
sizing.endpoint.modelMaxPromptTokens / 3 // consume one 3rd of the model window
53
);
54
55
const isTagBasedDocumentSummary = this.experimentationService.getTreatmentVariable<boolean>('copilotchat.tagBasedDocumentSummary') ?? false;
56
57
return {
58
summarizedDocument,
59
isIgnored,
60
priorities: promptPriorities,
61
tagBasedDocumentSummary: isTagBasedDocumentSummary
62
};
63
}
64
65
render(state: InlineChatNotebookBasePromptState, sizing: PromptSizing) {
66
if (this.props.documentContext.document.uri.scheme !== Schemas.vscodeNotebookCell) {
67
throw illegalArgument('InlineChatNotebookBasePrompt should be used only with a notebook!');
68
}
69
const { query, history, chatVariables } = this.props.promptContext;
70
const { language: lang } = this.props.documentContext;
71
const extractCodeBlock = lang.languageId !== 'markdown';
72
const jupyterNotebook = isJupyterNotebookUri(this.props.documentContext.document.uri);
73
74
const splitDoc = state.summarizedDocument.splitAroundOriginalSelectionEnd();
75
const { codeAbove, hasContent, codeBelow } = splitDoc;
76
const code = `${codeAbove}$PLACEHOLDER$${codeBelow}`;
77
const replyInterpreter = splitDoc.createReplyInterpreter(
78
LeadingMarkdownStreaming.Mute,
79
extractCodeBlock ? EarlyStopping.StopAfterFirstCodeBlock : EarlyStopping.None,
80
splitDoc.insertOrReplaceStreaming,
81
extractCodeBlock ? TextPieceClassifiers.createCodeBlockClassifier() : TextPieceClassifiers.createAlwaysInsideCodeBlockClassifier(),
82
line => line.value.trim() !== '$PLACEHOLDER$',
83
);
84
85
const priorities = state.priorities;
86
const tagBasedDocumentSummary = state.tagBasedDocumentSummary;
87
88
return (
89
<>
90
<meta value={new ReplyInterpreterMetaData(replyInterpreter)} />
91
<SystemMessage priority={priorities.core}>
92
You are an AI programming assistant.<br />
93
When asked for your name, you must respond with "GitHub Copilot".<br />
94
You are a world class expert in programming, and especially good at {lang.languageId}.<br />
95
Source code is always contained in ``` blocks.<br />
96
The user needs help to write some new code.<br />
97
<LegacySafetyRules />
98
</SystemMessage>
99
<HistoryWithInstructions inline={true} historyPriority={priorities.history ?? 700} passPriority history={history}>
100
<InstructionMessage priority={priorities.core}>
101
{jupyterNotebook &&
102
<>
103
<JupyterNotebookRules />
104
{!tagBasedDocumentSummary && <>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 /></>}
105
</>
106
}
107
{hasContent && <>The user includes existing code and marks with $PLACEHOLDER$ where the new code should go.<br /></>}
108
{hasContent && <>DO NOT include the text "$PLACEHOLDER$" in your reply.<br /></>}
109
{hasContent && <>DO NOT repeat any code from the user in your reply.<br /></>}
110
{(!hasContent && extractCodeBlock) && <>Your must generate a code block surrounded with ``` that will be used in a new file<br /></>}
111
{!extractCodeBlock && <>When generating content for markdown cell, provide the answer directly without any additional introductory text. Ensure that the response is structured in Markdown format to seamlessly integrate into the markdown file.</>}
112
</InstructionMessage>
113
</HistoryWithInstructions>
114
<UserMessage priority={priorities.context}>
115
<CustomInstructions languageId={lang.languageId} chatVariables={chatVariables} />
116
</UserMessage>
117
<ChatToolReferences priority={priorities.context} promptContext={this.props.promptContext} flexGrow={1} embeddedInsideUserMessage={false} />
118
<ChatVariables priority={priorities.context} chatVariables={chatVariables} embeddedInsideUserMessage={false} />
119
<InlineChatNotebookGenerateSelection documentContext={this.props.documentContext} hasContent={hasContent} code={code} priority={priorities.core} tagBasedDocumentSummary={tagBasedDocumentSummary} />
120
<InlineChatNotebookVariables notebookURI={this.props.documentContext.document.uri} priorities={priorities} query={query} />
121
<UserMessage priority={priorities.core}>
122
<UserQuery chatVariables={chatVariables} query={query} /><br />
123
{(hasContent && extractCodeBlock) && <>The code that would fit at $PLACEHOLDER$ with ``` is:</>}
124
{(hasContent && !extractCodeBlock) && <>The code that would fit at $PLACEHOLDER$ without ``` is:</>}
125
</UserMessage>
126
</>
127
);
128
}
129
}
130
131
interface InlineChatNotebookGenerateSelectionProps extends InlineChatNotebookSelectionCommonProps {
132
hasContent: boolean;
133
code: string;
134
tagBasedDocumentSummary: boolean;
135
}
136
137
class InlineChatNotebookGenerateSelection extends PromptElement<InlineChatNotebookGenerateSelectionProps, InlineChatNotebookSelectionState> {
138
constructor(
139
props: InlineChatNotebookGenerateSelectionProps,
140
@IConfigurationService private readonly configurationService: IConfigurationService,
141
@INotebookService private readonly notebookService: INotebookService,
142
@ITabsAndEditorsService private readonly tabsAndEditorsService: ITabsAndEditorsService,
143
@IExperimentationService private readonly experimentationService: IExperimentationService,
144
@IWorkspaceService private readonly workspaceService: IWorkspaceService
145
) {
146
super(props);
147
}
148
149
override async prepare(): Promise<InlineChatNotebookSelectionState> {
150
const { document, wholeRange } = this.props.documentContext;
151
152
const inSummaryExperiment = this.experimentationService.getTreatmentVariable('copilotchat.notebookSummary')
153
|| this.configurationService.getConfig(ConfigKey.Advanced.NotebookSummaryExperimentEnabled);
154
155
let executedCells: vscode.NotebookCell[] | undefined = undefined;
156
if (inSummaryExperiment && this.tabsAndEditorsService.activeNotebookEditor?.notebook && this.tabsAndEditorsService.activeNotebookEditor?.notebook.uri.path === document.uri.path) {
157
// experiment new notebook summary
158
executedCells = this.notebookService.getCellExecutions(this.tabsAndEditorsService.activeNotebookEditor.notebook.uri);
159
}
160
161
return {
162
wholeRange: document.validateRange(wholeRange),
163
executedCells
164
};
165
}
166
167
render(state: InlineChatNotebookSelectionState, sizing: PromptSizing) {
168
if (this.props.documentContext.document.uri.scheme !== Schemas.vscodeNotebookCell) {
169
throw illegalArgument('InlineChatNotebookSelection should be used only with a notebook!');
170
}
171
const { wholeRange } = state;
172
const contextInfo = generateSelectionContextInNotebook(
173
sizing.endpoint.modelMaxPromptTokens / 3, // consume one 3rd of the model window
174
this.props.documentContext,
175
wholeRange,
176
this.tabsAndEditorsService,
177
this.workspaceService
178
);
179
const doc = this.props.documentContext.document;
180
181
const jupyterNotebook = isJupyterNotebookUri(this.props.documentContext.document.uri);
182
const { hasContent, code } = this.props;
183
const { aboveCells, belowCells } = contextInfo;
184
const aboveCellsInfo = aboveCells || [];
185
const belowCellsInfo = belowCells || [];
186
const lang = this.props.documentContext.language;
187
const isMarkdown = lang.languageId === 'markdown';
188
const executedCells = state.executedCells || [];
189
190
const tagBasedDocumentSummary = this.props.tagBasedDocumentSummary;
191
192
return <>
193
{
194
jupyterNotebook
195
? <>
196
{
197
(executedCells.length > 0 &&
198
<InlineChatJupyterNotebookCellSummaryContextRenderer documentContext={this.props.documentContext} executedCells={executedCells} />
199
)
200
}
201
{
202
(executedCells.length === 0 && (aboveCellsInfo.length > 0 || belowCellsInfo.length > 0) && !tagBasedDocumentSummary) &&
203
<InlineChatJupyterNotebookCellsContextRenderer documentContext={this.props.documentContext} aboveCells={aboveCellsInfo} belowCells={belowCellsInfo} />
204
}
205
{
206
(executedCells.length === 0 && (aboveCellsInfo.length > 0 || belowCellsInfo.length > 0) && tagBasedDocumentSummary) &&
207
<InlineChatJupyterNotebookCellsContextTagBasedRenderer documentContext={this.props.documentContext} aboveCells={aboveCellsInfo} belowCells={belowCellsInfo} />
208
}
209
<UserMessage>
210
{
211
isMarkdown ?
212
<>
213
{hasContent && <>Now I edit a markdown cell in this Jupyter Notebook document at index {aboveCellsInfo.length}.<br /></>}
214
{!hasContent && <>Now I create a new markdown cell in this Jupyter Notebook document at index {aboveCellsInfo.length}.<br /></>}
215
This is a markdown cell. Markdown cell is used to describe and document your workflow.<br />
216
{hasContent && <><CodeBlock uri={doc.uri} languageId={lang.languageId} code={code} shouldTrim={false} /><br /></>}
217
</>
218
:
219
<>
220
{hasContent && <>Now I edit a cell in this Jupyter Notebook document at index {aboveCellsInfo.length}.<br /></>}
221
{!hasContent && <>Now I create a new cell in this Jupyter Notebook document at index {aboveCellsInfo.length}.<br /></>}
222
{hasContent && <><CodeBlock uri={doc.uri} languageId={lang.languageId} code={code} shouldTrim={false} /><br /></>}
223
</>
224
}
225
226
</UserMessage>
227
</>
228
: <>
229
<InlineChatCustomNotebookInfoRenderer documentContext={this.props.documentContext} />
230
<InlineChatCustomNotebookCellsContextRenderer documentContext={this.props.documentContext} aboveCells={aboveCellsInfo} belowCells={belowCellsInfo} />
231
<UserMessage>
232
{
233
isMarkdown ?
234
<>
235
{hasContent && <>Now I edit a markdown cell in this custom Notebook document at index {aboveCellsInfo.length}.<br /></>}
236
{!hasContent && <>Now I create a new markdown cell in this custom Notebook document at index {aboveCellsInfo.length}.<br /></>}
237
This is a markdown cell. Markdown cell is used to describe and document your workflow.<br />
238
{hasContent && <><CodeBlock uri={doc.uri} languageId={lang.languageId} code={code} shouldTrim={false} /><br /></>}
239
</>
240
:
241
<>
242
{hasContent && <>Now I edit a cell in this custom Notebook document at index {aboveCellsInfo.length}.<br /></>}
243
{!hasContent && <>Now I create a new cell in this custom Notebook document at index {aboveCellsInfo.length}.<br /></>}
244
{hasContent && <><CodeBlock uri={doc.uri} languageId={lang.languageId} code={code} shouldTrim={false} /><br /></>}
245
</>
246
}
247
</UserMessage>
248
</>
249
}
250
</>;
251
}
252
}
253
254
interface InlineChatJupyterNotebookCellSummaryContextRendererProps extends BasePromptElementProps {
255
documentContext: IDocumentContext;
256
executedCells: vscode.NotebookCell[] | undefined;
257
}
258
259
class InlineChatJupyterNotebookCellSummaryContextRenderer extends PromptElement<InlineChatJupyterNotebookCellSummaryContextRendererProps> {
260
render(state: void, sizing: PromptSizing) {
261
if (!isNotebookCellOrNotebookChatInput(this.props.documentContext.document.uri)) {
262
throw illegalArgument('InlineChatJupyterNotebookCellSummaryContextRenderer should be used only with a notebook!');
263
}
264
const lang = this.props.documentContext.language;
265
const executedCells = this.props.executedCells || [];
266
267
return (
268
<>
269
{
270
<UserMessage>
271
I am working on a Jupyter notebook.<br />
272
Users have executed the following cells in this notebook<br />
273
Each cell contains a code block started with ```{lang.languageId}<br />
274
Since it is Jupyter Notebook, if a module is already imported in a cell, it can be used in other cells as well.<br />
275
For the same reason, if a variable is defined in a cell, it can be used in other cells as well.<br />
276
We should not repeat the same import or variable definition in multiple cells, unless we want to overwrite the previous definition.<br />
277
Do not generate CELL INDEX in your answer, it is only used to help you understand the context.<br />
278
<br />
279
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 />
280
## Examples starts here<br />
281
Here are the executed cells in this Jupyter Notebook:<br />
282
```python<br />
283
import pandas as pd<br />
284
<br />
285
df = pd.DataFrame(&#123;'Name': ['Alice', 'Bob', 'Charlie'], 'Age': [25, 30, 35], 'Gender': ['F', 'M', 'M']&#125;)<br />
286
print(df)<br />
287
```<br />
288
---------------------------------<br />
289
USER:<br />
290
Now I create a new cell in this Jupyter Notebook document at index 1.<br />
291
USER:<br />
292
plot the data frame<br />
293
<br />
294
---------------------------------<br />
295
ChatGPT Answer<br />
296
---------------------------------<br />
297
To plot the dataframe, we can use the `plot()` method of pandas dataframe. Here's the code:<br />
298
<br />
299
```python<br />
300
df.plot(x='Name', y='Age', kind='bar')<br />
301
```<br />
302
## Example ends here<br />
303
{executedCells.map((cell) => (<NotebookCellContent cell={cell} />))}
304
</UserMessage>
305
}
306
</>
307
);
308
}
309
}
310
311
class NotebookCellContent extends PromptElement<{ cell: vscode.NotebookCell } & BasePromptElementProps> {
312
override render() {
313
return <>
314
```{this.props.cell.document.languageId}<br />
315
{this.props.cell.document.getText()}<br />
316
```
317
</>;
318
}
319
}
320