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