Path: blob/main/extensions/copilot/src/extension/prompts/node/codeMapper/codeMapperPrompt.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 { AssistantMessage, BasePromptElementProps, Chunk, PrioritizedList, PromptElement, PromptReference, PromptSizing, SystemMessage, UserMessage } from '@vscode/prompt-tsx';67import { NotebookDocumentSnapshot } from '../../../../platform/editing/common/notebookDocumentSnapshot';8import { TextDocumentSnapshot } from '../../../../platform/editing/common/textDocumentSnapshot';9import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';10import { IParserService } from '../../../../platform/parser/node/parserService';11import { getLanguageForResource } from '../../../../util/common/languages';12import { CharCode } from '../../../../util/vs/base/common/charCode';13import { StringEdit } from '../../../../util/vs/editor/common/core/edits/stringEdit';14import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';15import { Uri } from '../../../../vscodeTypes';16import { getStructure } from '../../../context/node/resolvers/selectionContextHelpers';17import { CompositeElement } from '../base/common';18import { ResponseTranslationRules } from '../base/responseTranslationRules';19import { LegacySafetyRules } from '../base/safetyRules';20import { Tag } from '../base/tag';21import { ProjectedDocument } from '../inline/summarizedDocument/summarizeDocument';22import { DocumentSummarizer, NotebookDocumentSummarizer, summarizeDocumentSync } from '../inline/summarizedDocument/summarizeDocumentHelpers';23import { EXISTING_CODE_MARKER } from '../panel/codeBlockFormattingRules';24import { fileVariableCostFn } from '../panel/fileVariable';25import { CodeBlock } from '../panel/safeElements';26import { UnsafeCodeBlock } from '../panel/unsafeElements';27import { ICodeMapperRequestInput, isNewDocument } from './codeMapper';28import { PatchEditExamplePatch, PatchEditInputCodeBlock, PatchEditRules } from './patchEditGeneration';2930export interface CodeMapperPromptProps extends BasePromptElementProps {31readonly request: ICodeMapperRequestInput;32readonly shouldTrimCodeBlocks?: boolean;33readonly inProgressRewriteContent?: string;34}3536export class CodeMapperPatchRewritePrompt extends PromptElement<CodeMapperPromptProps> {3738constructor(39props: CodeMapperPromptProps,40@IIgnoreService private readonly ignoreService: IIgnoreService,41@IParserService private readonly parserService: IParserService,42) {43super(props);44}4546async render(state: void, sizing: PromptSizing) {47if (isNewDocument(this.props.request)) {48// TODO@joyceerhl @aeschli remove the find/replace variant49return;50}5152const document = this.props.request.existingDocument;5354const isIgnored = await this.ignoreService.isCopilotIgnored(document.uri);55if (isIgnored) {56return <ignoredFiles value={[document.uri]} />;57}5859const inputDocCharLimit = (sizing.endpoint.modelMaxPromptTokens / 3) * 4; // consume one 3rd of the model window, estimating roughly 4 chars per token;60let projectedDocument: ProjectedDocument;61if (document.getText().length > inputDocCharLimit && document instanceof TextDocumentSnapshot) { // TODO@rebornix @DonJayamanne handle large notebook document62// only compute the summarized document if needed63const structure = await getStructure(this.parserService, document, undefined);64projectedDocument = summarizeDocumentSync(inputDocCharLimit, document, undefined, structure, { tryPreserveTypeChecking: false });65} else {66projectedDocument = new ProjectedDocument(document.getText(), StringEdit.empty, document.languageId);67}68const exampleUri = Uri.file('/someFolder/myFile.ts');69return (70<>71<references value={[new PromptReference(document.uri)]} />72<SystemMessage priority={1000}>73You are an AI programming assistant that is specialized in applying code changes to an existing document.<br />74I have a code block that represents a suggestion for a code change and I have a {document.languageId} file opened in a code editor.<br />75I expect you to come up with code changes that apply the code block to the editor.<br />76I want the changes to be applied in a way that is safe and does not break the existing code, is correctly indented and matching the code style.<br />77For the response, always follow these instructions:<br />781. Analyse the code block, the content of the editor and the current selection to decide if the code block should replace existing code or is to be inserted.<br />792. A line comment with `{EXISTING_CODE_MARKER}` indicates a section of code that has not changed<br />803. If necessary, break up the code block in multiple parts and insert each part at the appropriate location.<br />814. If necessary, make changes to other parts in the editor so that the final result is valid, properly formatted and indented.<br />825. Finally, provide the code modifications<br />83<PatchEditRules />84<br />85<LegacySafetyRules />86<ResponseTranslationRules />87<Tag name='example' priority={100}>88<Tag name='user'>8990I have the following code open in the editor.<br />91<PatchEditInputCodeBlock92uri={exampleUri}93languageId='typescript'94code={[`import { readFileSync } from 'fs';`, '', 'class C { }']}95/>96< br />97This is the code block that represents a suggestion for a code change:<br />98<UnsafeCodeBlock code={'private _stream: Stream;'} languageId={'typescript'} includeFilepath={false} />99< br />100Please find out how the code block can be applied to the editor.101</Tag>102<Tag name='assistant'>103<PatchEditExamplePatch104changes={105[106{107uri: exampleUri,108find: [`import { readFileSync } from 'fs';`,],109replace: [`import { readFileSync } from 'fs';`, `import { Stream } from 'stream';`]110},111{112uri: exampleUri,113find: ['class C { }'],114replace: ['class C {', '\tprivate _stream: Stream;', '}']115},116117]118}119/>120</Tag>121</Tag>122</SystemMessage>123<UserMessage priority={700}>124<CompositeElement /*priority={600} TODO@aeschli commented out for fixed prompt-tsx issue */>125{126projectedDocument.text.length > 0 ?127<>128I have the following code open in the editor, starting from line 1 to line {projectedDocument.lineCount}.<br />129</> :130<>131I am in an empty file:<br />132</>133}134<PatchEditInputCodeBlock uri={document.uri} languageId={document.languageId} code={projectedDocument.text} shouldTrim={false} /><br />135</CompositeElement >136<CodeBlockChangeDescription markdownBeforeBlock={getLastSentence(this.props.request.markdownBeforeBlock)} />137This is the code block that represents a suggestion for a code change:<br />138<CodeBlock uri={document.uri} languageId={document.languageId} code={this.props.request.codeBlock} shouldTrim={false} includeFilepath={false} /><br />139<Tag name='userPrompt'>140Please find out how the code block can be applied to the editor. Provide the code changes in the format as described above.<br />141</Tag>142</UserMessage >143</>144);145}146}147148interface CodeBlockChangeDescriptionProps extends BasePromptElementProps {149readonly markdownBeforeBlock?: string;150}151152class CodeBlockChangeDescription extends PromptElement<CodeBlockChangeDescriptionProps> {153render() {154if (this.props.markdownBeforeBlock) {155return (<>156This is the description of what the code block changes:<br />157<Tag name='changeDescription'>158{this.props.markdownBeforeBlock}159</Tag>160<br />161</>162);163}164return undefined;165}166}167168export class CodeMapperFullRewritePrompt extends PromptElement<CodeMapperPromptProps> {169constructor(170props: CodeMapperPromptProps,171@IIgnoreService private readonly ignoreService: IIgnoreService,172@IInstantiationService private readonly instantiationService: IInstantiationService,173) {174super(props);175}176177async render(state: void, sizing: PromptSizing) {178179const shouldTrimCodeBlocks = this.props.shouldTrimCodeBlocks ?? false;180if (isNewDocument(this.props.request)) {181const validDocumentContext = [];182for (const context of this.props.request.workingSet) {183const isIgnored = await this.ignoreService.isCopilotIgnored(context.uri);184if (!isIgnored) {185validDocumentContext.push(context);186}187}188189return (<>190<references value={validDocumentContext.map(document => new PromptReference(document.uri))} />191<SystemMessage priority={1000}>192You are an AI programming assistant that is specialized in generating code for a new document.< br />193<LegacySafetyRules />194The user has a code block that represents a suggestion for the contents of a single new file, and several other files opened in a code editor.<br />195The provided files may contain code relevant to the new file. Consider them when generating the new file.<br />196For the response, always follow these instructions:<br />1971. Analyse the code block and the existing documents to decide which parts of the existing document should be incorporated in the generated code.<br />1982. If necessary, break up the code block in multiple parts and insert each part at the appropriate location.< br />1993. Preserve whitespace and newlines right after the parts of the file that you modify.<br />2004. The final result must be syntactically valid, properly formatted, and correctly indented. It should not contain any {EXISTING_CODE_MARKER} comments.<br />2015. Finally, provide the full contents of the new file.<br />202</SystemMessage>203<UserMessage priority={700}>204<PrioritizedList priority={690} descending={true}>205{/* Skip empty files since they contain no useful context */}206{validDocumentContext.map(document => (document.lineCount === 0 ? undefined : <Chunk>207<>208I have the following code from the file {document.uri.toString()} open in the editor, starting from line 1 to line {document.lineCount}.<br />209<CodeBlock uri={document.uri} languageId={document.languageId} code={document.getText()} shouldTrim={shouldTrimCodeBlocks} /><br />210</>211</Chunk>))}212</PrioritizedList>213<Chunk priority={695}>214<CodeBlockChangeDescription markdownBeforeBlock={this.props.request.markdownBeforeBlock} />215This is the code block that represents the suggested code change:<br />216<CodeBlock uri={this.props.request.uri} languageId={getLanguageForResource(this.props.request.uri).languageId} code={this.props.request.codeBlock} shouldTrim={shouldTrimCodeBlocks} /><br />217<Tag name='userPrompt'>218Provide the contents of the new file.219</Tag>220</Chunk>221</UserMessage>222{this.props.inProgressRewriteContent && <>223<AssistantMessage priority={800}>224{this.props.inProgressRewriteContent}225</AssistantMessage>226<UserMessage priority={900}>227Please continue providing the next part of the response.228</UserMessage>229</>}230</>);231}232233const document = this.props.request.existingDocument;234const isIgnored = await this.ignoreService.isCopilotIgnored(document.uri);235if (isIgnored) {236return <ignoredFiles value={[document.uri]} />;237}238239const summarized = document instanceof NotebookDocumentSnapshot ?240await this.instantiationService.createInstance(NotebookDocumentSummarizer).summarizeDocument(document, undefined, undefined, sizing.tokenBudget, {241costFnOverride: fileVariableCostFn,242}) :243await this.instantiationService.createInstance(DocumentSummarizer).summarizeDocument(document, undefined, undefined, sizing.tokenBudget, {244costFnOverride: fileVariableCostFn,245});246const code = summarized.text;247248return (249<>250<references value={[new PromptReference(document.uri)]} />251<SystemMessage priority={1000}>252You are an AI programming assistant that is specialized in applying code changes to an existing document.< br />253<LegacySafetyRules />254The user has a code block that represents a suggestion for a code change and a {document.languageId} file opened in a code editor.<br />255Rewrite the existing document to fully incorporate the code changes in the provided code block.<br />256For the response, always follow these instructions:<br />2571. Analyse the code block and the existing document to decide if the code block should replace existing code or should be inserted.<br />2582. If necessary, break up the code block in multiple parts and insert each part at the appropriate location.< br />2593. Preserve whitespace and newlines right after the parts of the file that you modify.<br />2604. The final result must be syntactically valid, properly formatted, and correctly indented. It should not contain any ...existing code... comments.<br />2615. Finally, provide the fully rewritten file. You must output the complete file.<br />262</SystemMessage>263<UserMessage priority={700}>264{265document.lineCount > 0 ?266<>267I have the following code open in the editor, starting from line 1 to line {document.lineCount}.<br />268<CodeBlock uri={document.uri} languageId={document.languageId} code={code} shouldTrim={shouldTrimCodeBlocks} /><br />269</> :270<>271I am in an empty editor.272</>273}274<CodeBlockChangeDescription markdownBeforeBlock={this.props.request.markdownBeforeBlock} />275This is the code block that represents the suggested code change:<br />276<CodeBlock uri={document.uri} languageId={document.languageId} code={this.props.request.codeBlock} shouldTrim={shouldTrimCodeBlocks} /><br />277<Tag name='userPrompt'>278Provide the fully rewritten file, incorporating the suggested code change. You must produce the complete file.279</Tag>280</UserMessage>281{this.props.inProgressRewriteContent && <>282<AssistantMessage priority={800}>283{this.props.inProgressRewriteContent}284</AssistantMessage>285<UserMessage priority={900}>286Please continue providing the next part of the response.287</UserMessage>288</>}289</>290);291}292}293294function getLastSentence(markdownBeforeBlock?: string): string | undefined {295if (markdownBeforeBlock) {296const whitespaces = [CharCode.Space, CharCode.Tab, CharCode.LineFeed, CharCode.CarriageReturn];297const newlines = [CharCode.LineFeed, CharCode.CarriageReturn];298let end = markdownBeforeBlock.length;299while (end > 0 && whitespaces.includes(markdownBeforeBlock.charCodeAt(end - 1))) {300end--;301}302let start = end;303while (start > 0 && !newlines.includes(markdownBeforeBlock.charCodeAt(start - 1))) {304start--;305}306if (start < end) {307return markdownBeforeBlock.substring(start, end);308}309310}311return undefined;312}313314315