Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompts/node/inline/inlineChatNotebookEditPrompt.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, PromptSizing, SystemMessage, TextChunk, UserMessage } from '@vscode/prompt-tsx';
7
import type { NotebookDocument } from 'vscode';
8
import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';
9
import { IParserService } from '../../../../platform/parser/node/parserService';
10
import { ITabsAndEditorsService } from '../../../../platform/tabs/common/tabsAndEditorsService';
11
import { IExperimentationService } from '../../../../platform/telemetry/common/nullExperimentationService';
12
import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService';
13
import { findNotebook, isJupyterNotebookUri } from '../../../../util/common/notebooks';
14
import { illegalArgument } from '../../../../util/vs/base/common/errors';
15
import { Schemas } from '../../../../util/vs/base/common/network';
16
import { SelectionSplitKind, SummarizedDocumentData } from '../../../intents/node/testIntent/summarizedDocumentWithSelection';
17
import { IDocumentContext } from '../../../prompt/node/documentContext';
18
import { EarlyStopping, LeadingMarkdownStreaming, ReplyInterpreterMetaData } from '../../../prompt/node/intents';
19
import { TextPieceClassifiers } from '../../../prompt/node/streamingEdits';
20
import { InstructionMessage } from '../base/instructionMessage';
21
import { LegacySafetyRules } from '../base/safetyRules';
22
import { JupyterNotebookRules } from '../notebook/commonPrompts';
23
import { ChatToolReferences, ChatVariables, UserQuery } from '../panel/chatVariables';
24
import { HistoryWithInstructions } from '../panel/conversationHistory';
25
import { CustomInstructions } from '../panel/customInstructions';
26
import { CodeBlock } from '../panel/safeElements';
27
import { InlineChatEditCodePromptProps } from './inlineChatEditCodePrompt';
28
import { promptPriorities } from './inlineChatNotebookCommon';
29
import { generateSelectionContextInNotebook, InlineChatCustomNotebookCellsContextRenderer, InlineChatCustomNotebookInfoRenderer, InlineChatJupyterNotebookCellsContextRenderer, InlineChatNotebookBasePromptState, InlineChatNotebookSelectionCommonProps, InlineChatNotebookSelectionState, InlineChatNotebookVariables } from './inlineChatNotebookCommonPromptElements';
30
import { createPromptingSummarizedDocument } from './promptingSummarizedDocument';
31
32
interface InlineChatNotebookEditSelectionProps extends InlineChatNotebookSelectionCommonProps {
33
hasCodeWithoutSelection: boolean;
34
codeWithoutSelection: string;
35
codeSelected: string;
36
tagBasedDocumentSummary: boolean;
37
}
38
39
class InlineChatNotebookEditSelection extends PromptElement<InlineChatNotebookEditSelectionProps, InlineChatNotebookSelectionState> {
40
constructor(
41
props: InlineChatNotebookEditSelectionProps,
42
@ITabsAndEditorsService private readonly tabsAndEditorsService: ITabsAndEditorsService,
43
@IWorkspaceService private readonly workspaceService: IWorkspaceService
44
) {
45
super(props);
46
}
47
48
override async prepare(): Promise<InlineChatNotebookSelectionState> {
49
const { document, wholeRange } = this.props.documentContext;
50
return {
51
wholeRange: document.validateRange(wholeRange)
52
};
53
}
54
55
render(state: InlineChatNotebookSelectionState, sizing: PromptSizing) {
56
if (this.props.documentContext.document.uri.scheme !== Schemas.vscodeNotebookCell) {
57
throw illegalArgument('InlineChatNotebookSelection should be used only with a notebook!');
58
}
59
const { wholeRange } = state;
60
const contextInfo = generateSelectionContextInNotebook(
61
sizing.endpoint.modelMaxPromptTokens / 3, // consume one 3rd of the model window
62
this.props.documentContext,
63
wholeRange,
64
this.tabsAndEditorsService,
65
this.workspaceService
66
);
67
const doc = this.props.documentContext.document;
68
69
const jupyterNotebook = isJupyterNotebookUri(this.props.documentContext.document.uri);
70
const { hasCodeWithoutSelection, codeWithoutSelection, codeSelected } = this.props;
71
const { aboveCells, belowCells } = contextInfo;
72
const aboveCellsInfo = aboveCells || [];
73
const belowCellsInfo = belowCells || [];
74
const lang = this.props.documentContext.language;
75
const isMarkdown = lang.languageId === 'markdown';
76
77
const isEditing = hasCodeWithoutSelection || codeSelected.length > 0;
78
const tagBasedDocumentSummary = this.props.tagBasedDocumentSummary;
79
80
return <>
81
{
82
jupyterNotebook
83
? <>
84
{
85
((aboveCellsInfo.length > 0 || belowCellsInfo.length > 0) && !tagBasedDocumentSummary) &&
86
<InlineChatJupyterNotebookCellsContextRenderer documentContext={this.props.documentContext} aboveCells={aboveCellsInfo} belowCells={belowCellsInfo} />
87
}
88
{
89
((aboveCellsInfo.length > 0 || belowCellsInfo.length > 0) && tagBasedDocumentSummary) &&
90
<InlineChatJupyterNotebookCellsContextRenderer documentContext={this.props.documentContext} aboveCells={aboveCellsInfo} belowCells={belowCellsInfo} />
91
}
92
<UserMessage>
93
{
94
isMarkdown ?
95
<>
96
{isEditing && <>Now I edit a markdown cell in this Jupyter Notebook document at index {aboveCellsInfo.length}.<br /></>}
97
{!isEditing && <>Now I create a new markdown cell in this Jupyter Notebook document at index {aboveCellsInfo.length}.<br /></>}
98
This is a markdown cell. Markdown cell is used to describe and document your workflow.<br />
99
{hasCodeWithoutSelection && <><CodeBlock uri={doc.uri} languageId={lang.languageId} code={codeWithoutSelection} shouldTrim={false} /><br /></>}
100
{hasCodeWithoutSelection && <>The $SELECTION_PLACEHOLDER$ code is:<br /></>}
101
<CodeBlock uri={doc.uri} languageId={lang.languageId} code={codeSelected} shouldTrim={false} />
102
</>
103
:
104
<>
105
{isEditing && <>Now I edit a cell in this Jupyter Notebook document at index {aboveCellsInfo.length}.<br /></>}
106
{!isEditing && <>Now I create a new cell in this Jupyter Notebook document at index {aboveCellsInfo.length}.<br /></>}
107
{hasCodeWithoutSelection && <><CodeBlock uri={doc.uri} languageId={lang.languageId} code={codeWithoutSelection} shouldTrim={false} /><br /></>}
108
{(codeSelected.length > 0) && <>The $SELECTION_PLACEHOLDER$ code is:<br /></>}
109
<CodeBlock uri={doc.uri} languageId={lang.languageId} code={codeSelected} shouldTrim={false} />
110
</>
111
}
112
113
</UserMessage>
114
</>
115
: <>
116
<InlineChatCustomNotebookInfoRenderer documentContext={this.props.documentContext} />
117
<InlineChatCustomNotebookCellsContextRenderer documentContext={this.props.documentContext} aboveCells={aboveCellsInfo} belowCells={belowCellsInfo} />
118
<UserMessage>
119
{
120
isMarkdown ?
121
<>
122
{hasCodeWithoutSelection && <>Now I edit a markdown cell in this custom Notebook document at index {aboveCellsInfo.length}.<br /></>}
123
{!hasCodeWithoutSelection && <>Now I create a new markdown cell in this custom Notebook document at index {aboveCellsInfo.length}.<br /></>}
124
This is a markdown cell. Markdown cell is used to describe and document your workflow.<br />
125
{hasCodeWithoutSelection && <><CodeBlock uri={doc.uri} languageId={lang.languageId} code={codeWithoutSelection} shouldTrim={false} /><br /></>}
126
{hasCodeWithoutSelection && <>The $SELECTION_PLACEHOLDER$ code is:<br /></>}
127
<CodeBlock uri={doc.uri} languageId={lang.languageId} code={codeSelected} shouldTrim={false} />
128
</>
129
:
130
<>
131
{hasCodeWithoutSelection && <>Now I edit a cell in this custom Notebook document at index {aboveCellsInfo.length}.<br /></>}
132
{!hasCodeWithoutSelection && <>Now I create a new cell in this custom Notebook document at index {aboveCellsInfo.length}.<br /></>}
133
{hasCodeWithoutSelection && <><CodeBlock uri={doc.uri} languageId={lang.languageId} code={codeWithoutSelection} shouldTrim={false} /><br /></>}
134
{hasCodeWithoutSelection && <>The $SELECTION_PLACEHOLDER$ code is:<br /></>}
135
<CodeBlock uri={doc.uri} languageId={lang.languageId} code={codeSelected} shouldTrim={false} />
136
</>
137
}
138
</UserMessage>
139
</>
140
}
141
</>;
142
}
143
}
144
145
interface InlineChatNotebookAlternativeEditPromptState extends InlineChatNotebookBasePromptState {
146
notebook?: NotebookDocument;
147
activeDocumentContext: IDocumentContext;
148
isJupyterNotebook: boolean;
149
}
150
151
export class InlineChatNotebookEditPrompt extends PromptElement<InlineChatEditCodePromptProps, InlineChatNotebookAlternativeEditPromptState> {
152
constructor(
153
props: InlineChatEditCodePromptProps,
154
@IIgnoreService private readonly ignoreService: IIgnoreService,
155
@IParserService private readonly parserService: IParserService,
156
@IExperimentationService private readonly experimentationService: IExperimentationService,
157
@IParserService private readonly _parserService: IParserService,
158
@IWorkspaceService private readonly workspaceService: IWorkspaceService
159
) {
160
super(props);
161
}
162
163
override async prepare(sizing: PromptSizing): Promise<InlineChatNotebookAlternativeEditPromptState> {
164
const currentDocumentContext = this.props.documentContext;
165
const activeDocumentContext = currentDocumentContext;
166
const notebook = findNotebook(currentDocumentContext.document.uri, this.workspaceService.notebookDocuments);
167
168
const isIgnored = await this.ignoreService.isCopilotIgnored(activeDocumentContext.document.uri);
169
const wholeRange = activeDocumentContext.document.validateRange(activeDocumentContext.wholeRange);
170
171
const summarizedDocument = await createPromptingSummarizedDocument(
172
this.parserService,
173
activeDocumentContext.document,
174
activeDocumentContext.fileIndentInfo,
175
wholeRange,
176
sizing.endpoint.modelMaxPromptTokens / 3 // consume one 3rd of the model window
177
);
178
179
const isTagBasedDocumentSummary = this.experimentationService.getTreatmentVariable<boolean>('copilotchat.tagBasedDocumentSummary') ?? false;
180
181
return {
182
notebook,
183
isJupyterNotebook: isJupyterNotebookUri(currentDocumentContext.document.uri),
184
summarizedDocument,
185
isIgnored,
186
priorities: promptPriorities,
187
tagBasedDocumentSummary: isTagBasedDocumentSummary,
188
activeDocumentContext: activeDocumentContext,
189
};
190
}
191
192
async render(state: InlineChatNotebookAlternativeEditPromptState, sizing: PromptSizing) {
193
if (!state.notebook) {
194
throw illegalArgument('InlineChatNotebookEditPrompt should be used only with a notebook!');
195
}
196
197
const context = state.activeDocumentContext;
198
const promptContext = this.props.promptContext;
199
if (context.document.uri.scheme !== Schemas.vscodeNotebookCell) {
200
throw illegalArgument('InlineChatNotebookEditPrompt should be used only with a notebook!');
201
}
202
if (state.isIgnored) {
203
return <ignoredFiles value={[context.document.uri]} />;
204
}
205
206
const tagBasedDocumentSummary = state.tagBasedDocumentSummary;
207
208
const { query, history, chatVariables } = promptContext;
209
const jupyterNotebook = state.isJupyterNotebook;
210
const document = context.document;
211
const lang = context.language;
212
const isMarkdown = lang.languageId === 'markdown';
213
const splitDoc = state.summarizedDocument.splitAroundAdjustedSelection();
214
const { codeAbove, codeSelected, codeBelow, hasCodeWithoutSelection } = splitDoc;
215
const data = await SummarizedDocumentData.create(this._parserService, document, context.fileIndentInfo, context.wholeRange, SelectionSplitKind.Adjusted);
216
const codeWithoutSelection = `${codeAbove}${data.placeholderText}${codeBelow}`;
217
const replyInterpreter = splitDoc.createReplyInterpreter(
218
LeadingMarkdownStreaming.Mute,
219
EarlyStopping.StopAfterFirstCodeBlock,
220
splitDoc.replaceSelectionStreaming,
221
TextPieceClassifiers.createCodeBlockClassifier(),
222
line => line.value.trim() !== data.placeholderText
223
);
224
225
const priorities = state.priorities;
226
return (
227
<>
228
<meta value={new ReplyInterpreterMetaData(replyInterpreter)} />
229
<SystemMessage priority={priorities.core}>
230
You are an AI programming assistant.<br />
231
When asked for your name, you must respond with "GitHub Copilot".<br />
232
You are a world class expert in programming, and especially good at {lang.languageId}.<br />
233
Source code is always contained in ``` blocks.<br />
234
The user needs help to modify some code.<br />
235
{hasCodeWithoutSelection && <>The user includes existing code and marks with {data.placeholderText} where the selected code should go.<br /></>}
236
<LegacySafetyRules />
237
</SystemMessage>
238
<HistoryWithInstructions inline={true} passPriority historyPriority={priorities.history ?? 700} history={history}>
239
<InstructionMessage priority={priorities.core}>
240
{jupyterNotebook &&
241
<>
242
<JupyterNotebookRules />
243
{!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 /></>}
244
</>
245
}
246
{isMarkdown && <>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.</>}
247
{hasCodeWithoutSelection && <>The user includes existing code and marks with {data.placeholderText} where the selected code should go.<br /></>}
248
</InstructionMessage>
249
</HistoryWithInstructions>
250
<ChatToolReferences priority={priorities.context} promptContext={promptContext} flexGrow={1} embeddedInsideUserMessage={false} />
251
<ChatVariables priority={priorities.context} chatVariables={chatVariables} embeddedInsideUserMessage={false} />
252
<InlineChatNotebookEditSelection documentContext={context} hasCodeWithoutSelection={hasCodeWithoutSelection} codeWithoutSelection={codeWithoutSelection} codeSelected={codeSelected} priority={priorities.core} tagBasedDocumentSummary={tagBasedDocumentSummary} />
253
<InlineChatNotebookVariables notebookURI={context.document.uri} priority={priorities.runtimeCore} priorities={priorities} query={query} />
254
<UserMessage>
255
<CustomInstructions priority={priorities.context} languageId={lang.languageId} chatVariables={chatVariables} />
256
<UserQuery priority={priorities.core} chatVariables={chatVariables} query={query} /><br />
257
{(hasCodeWithoutSelection && isMarkdown) && <TextChunk priority={priorities.core} >The modified {data.placeholderText} code without ``` is:</TextChunk>}
258
{(hasCodeWithoutSelection && !isMarkdown) && <TextChunk priority={priorities.core} >The modified {data.placeholderText} code with ``` is:</TextChunk>}
259
</UserMessage>
260
</>
261
);
262
}
263
}
264
265