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