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