Path: blob/main/extensions/copilot/src/platform/inlineEdits/common/statelessNextEditProvider.ts
13400 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 { Raw } from '@vscode/prompt-tsx';6import { Result } from '../../../util/common/result';7import { assert, assertNever } from '../../../util/vs/base/common/assert';8import { DeferredPromise } from '../../../util/vs/base/common/async';9import { CancellationToken, CancellationTokenSource } from '../../../util/vs/base/common/cancellation';10import { URI } from '../../../util/vs/base/common/uri';11import { LineEdit, LineReplacement, SerializedLineEdit } from '../../../util/vs/editor/common/core/edits/lineEdit';12import { StringEdit } from '../../../util/vs/editor/common/core/edits/stringEdit';13import { Position } from '../../../util/vs/editor/common/core/position';14import { OffsetRange } from '../../../util/vs/editor/common/core/ranges/offsetRange';15import { StringText } from '../../../util/vs/editor/common/core/text/abstractText';16import { ChatFetchResponseType, FetchResponse } from '../../chat/common/commonTypes';17import { ILogger } from '../../log/common/logService';18import { ISerializedOffsetRange, LogEntry, serializeOffsetRange } from '../../workspaceRecorder/common/workspaceLog';19import { DocumentId } from './dataTypes/documentId';20import { Edits } from './dataTypes/edit';21import { SerializedEdit } from './dataTypes/editUtils';22import { LanguageId } from './dataTypes/languageId';23import { DebugRecorderBookmark } from './debugRecorderBookmark';24import { InlineEditRequestLogContext } from './inlineEditLogContext';25import { stringifyChatMessages } from './utils/stringifyChatMessages';26import { IXtabHistoryEntry } from './workspaceEditTracker/nesXtabHistoryTracker';2728export type EditStreaming = AsyncGenerator<StreamedEdit, NoNextEditReason, void>;2930export class WithStatelessProviderTelemetry<T> {31constructor(32public readonly v: T,33public readonly telemetryBuilder: IStatelessNextEditTelemetry,34) {35}36}3738export type EditStreamingWithTelemetry = AsyncGenerator<WithStatelessProviderTelemetry<StreamedEdit>, WithStatelessProviderTelemetry<NoNextEditReason>, void>;3940export type StreamedEdit = {41readonly targetDocument: DocumentId;42readonly edit: LineReplacement;43readonly isFromCursorJump: boolean;44readonly window?: OffsetRange;45/**46* For cursor jump edits, this is the edit window around the original cursor position47* (before the jump). This allows the cached edit to be served when the cursor is48* in either the original location or the jump target location.49*/50readonly originalWindow?: OffsetRange;51};5253export type PushEdit = (edit: Result<StreamedEdit, NoNextEditReason>) => void;5455export class RequestEditWindow {56constructor(readonly window: OffsetRange) { }57containsCursor(cursor: OffsetRange): boolean {58return this.window.containsRange(cursor);59}60}6162export class RequestEditWindowWithCursorJump {63constructor(readonly window: OffsetRange, readonly originalWindow: OffsetRange) { }64containsCursor(cursor: OffsetRange): boolean {65return this.window.containsRange(cursor) || this.originalWindow.containsRange(cursor);66}67}6869export interface IStatelessNextEditProvider {70readonly ID: string;71provideNextEdit(request: StatelessNextEditRequest, logger: ILogger, logContext: InlineEditRequestLogContext, cancellationToken: CancellationToken): EditStreamingWithTelemetry;72handleAcceptance?(): void;73handleRejection?(): void;74handleIgnored?(): void;75}7677export class StatelessNextEditRequest<TFirstEdit = any> {7879private static ID = 0;80public readonly seqid = String(++StatelessNextEditRequest.ID);8182public readonly cancellationTokenSource = new CancellationTokenSource();83public liveDependentants = 0; // number of invocations which haven't been canceled and depend on this request84public fetchIssued = false;85public intermediateUserEdit: StringEdit | undefined = StringEdit.empty;8687/**88* Set by the stateless provider early in its execution (before any async work).89* Used to check whether a new cursor position falls within the edit window when90* deciding whether to reuse an in-flight request.91*/92public requestEditWindow: RequestEditWindow | RequestEditWindowWithCursorJump | undefined;9394private readonly _result: DeferredPromise<StatelessNextEditResult> = new DeferredPromise<StatelessNextEditResult>();95public get result(): Promise<StatelessNextEditResult> {96return this._result.p;97}9899constructor(100public readonly headerRequestId: string,101public readonly opportunityId: string,102/** this's the active document current contents (not sure "before" which edits this's named after -- maybe NES edits) */103public readonly documentBeforeEdits: StringText,104public readonly documents: readonly StatelessNextEditDocument[],105public readonly activeDocumentIdx: number,106public readonly xtabEditHistory: readonly IXtabHistoryEntry[],107public readonly firstEdit: DeferredPromise<Result<TFirstEdit, NoNextEditReason>>,108public readonly expandedEditWindowNLines: number | undefined,109public readonly isSpeculative: boolean,110public readonly logContext: InlineEditRequestLogContext,111public readonly recordingBookmark: DebugRecorderBookmark | undefined,112public readonly recording: LogEntry[] | undefined,113public readonly providerRequestStartDateTime: number | undefined,114) {115assert(documents.length > 0);116assert(activeDocumentIdx >= 0 && activeDocumentIdx < documents.length);117}118119public setResult(nextEditResult: StatelessNextEditResult) {120this._result.complete(nextEditResult);121}122123public setResultError(err: any) {124this._result.error(err);125}126127public hasDocument(docId: DocumentId): boolean {128return this.documents.find(d => d.id === docId) !== undefined;129}130131getActiveDocument(): StatelessNextEditDocument {132return this.documents[this.activeDocumentIdx];133}134135serialize(): ISerializedNextEditRequest {136return {137id: this.headerRequestId,138documents: this.documents.map(d => d.serialize()),139activeDocumentIdx: this.activeDocumentIdx,140recording: this.recording,141};142}143144toString(): string {145return this.toMarkdown();146}147148toMarkdown(): string {149const docs = this.documents.map((d, idx) => ` * [${idx + 1}/${this.documents.length}] ${idx === this.activeDocumentIdx ? '(active document) ' : ''}` + d.toMarkdown()).join('\n\n');150return `### StatelessNextEditRequest\n\n${docs}`;151}152}153154export interface ISerializedNextEditRequest {155id: string;156documents: ISerializedNextEditDocument[];157activeDocumentIdx: number;158recording: LogEntry[] | undefined;159}160161export class StatelessNextEditDocument {162public readonly documentAfterEdits = new StringText(this.recentEdits.apply(this.documentBeforeEdits.value));163public readonly documentAfterEditsLines: string[] = this.documentAfterEdits.getLines();164165/**166* NOTE: if you add new public fields to this class, please also update {@link ISerializedNextEditDocument} and {@link serialize()} methods,167* which are used to send this to http-server-powered NES provider.168*/169constructor(170public readonly id: DocumentId,171public readonly workspaceRoot: URI | undefined,172public readonly languageId: LanguageId,173public readonly documentLinesBeforeEdit: string[],174public readonly recentEdit: LineEdit,175public readonly documentBeforeEdits: StringText,176public readonly recentEdits: Edits,177public readonly lastSelectionInAfterEdit: OffsetRange | undefined = undefined,178) { }179180serialize(): ISerializedNextEditDocument {181return {182id: this.id.uri,183workspaceRoot: this.workspaceRoot?.toString(),184languageId: this.languageId,185documentLinesBeforeEdit: this.documentLinesBeforeEdit,186recentEdit: this.recentEdit.serialize(),187documentBeforeEdits: this.documentBeforeEdits.value,188recentEdits: this.recentEdits.serialize(),189lastSelectionInAfterEdit: this.lastSelectionInAfterEdit === undefined ? undefined : serializeOffsetRange(this.lastSelectionInAfterEdit),190};191}192193toString(): string {194return this.toMarkdown();195}196197toMarkdown(): string {198const lines: string[] = [];199200lines.push(`StatelessNextEditDocument: **${this.id.uri}**\n`);201lines.push('```patch');202lines.push(this.recentEdit.humanReadablePatch(this.documentLinesBeforeEdit));203lines.push('```');204lines.push('');205206return lines.join('\n');207}208}209210export interface ISerializedNextEditDocument {211id: string;212workspaceRoot: string | undefined;213languageId: string;214documentLinesBeforeEdit: string[];215recentEdit: SerializedLineEdit;216documentBeforeEdits: string;217recentEdits: SerializedEdit[];218lastSelectionInAfterEdit: ISerializedOffsetRange | undefined;219}220221export enum FilteredOutReason {222LowLogProbSuggestions = 'lowLogProbSuggestions',223EnforcingNextEditOptions = 'enforcingNextEditOptions',224PromptTooLarge = 'promptTooLarge',225Uncategorized = 'uncategorized',226}227228export namespace NoNextEditReason {229abstract class NoNextEditReason {230abstract toString(): string;231}232export class ActiveDocumentHasNoEdits extends NoNextEditReason {233public readonly kind = 'activeDocumentHasNoEdits';234235toString(): string {236return this.kind;237}238}239export class NoSuggestions extends NoNextEditReason {240public readonly kind = 'noSuggestions';241242constructor(243public readonly documentBeforeEdits: StringText,244public readonly window: OffsetRange | undefined,245public readonly nextCursorPosition?: Position | undefined,246public readonly nextCursorDocumentId?: DocumentId | undefined,247) {248super();249}250251toString(): string {252return this.kind;253}254}255export class GotCancelled extends NoNextEditReason {256public readonly kind = 'gotCancelled';257constructor(public readonly message: string | 'afterDebounce' | 'afterGettingEndpoint' | 'afterLanguageContextAwait' | 'afterPromptConstruction' | 'afterFetchCall' | 'duringStreaming' | 'afterResponse' | 'afterFailedRebase' | 'beforeExecutingNewRequest' | 'afterArtificialDelay' | 'afterNextCursorPredictionFetch') {258super();259}260261toString(): string {262return `${this.kind}:${this.message}`;263}264}265export class FetchFailure extends NoNextEditReason {266public readonly kind = 'fetchFailure';267constructor(public readonly error: Error) {268super();269}270toString(): string {271return `${this.kind}:${this.error.message}`;272}273}274export class FilteredOut extends NoNextEditReason {275public readonly kind = 'filteredOut';276constructor(public readonly message: FilteredOutReason | string) {277super();278}279toString(): string {280return `${this.kind}:${this.message}`;281}282}283export class PromptTooLarge extends NoNextEditReason {284public readonly kind = 'promptTooLarge';285constructor(public readonly message: 'editWindow' | 'currentFile' | 'final') {286super();287}288toString(): string {289return `${this.kind}:${this.message}`;290}291}292export class Uncategorized extends NoNextEditReason {293public readonly kind = 'uncategorized';294constructor(public readonly error: Error) {295super();296}297toString(): string {298return `${this.kind}:${this.error.message}`;299}300}301export class Unexpected extends NoNextEditReason {302public readonly kind = 'unexpected';303constructor(public readonly error: Error) {304super();305}306toString(): string {307return `${this.kind}:${this.error.message}`;308}309}310}311312export type NoNextEditReason =313| NoNextEditReason.ActiveDocumentHasNoEdits314| NoNextEditReason.NoSuggestions315| NoNextEditReason.GotCancelled316| NoNextEditReason.FetchFailure317| NoNextEditReason.FilteredOut318| NoNextEditReason.PromptTooLarge319| NoNextEditReason.Uncategorized320| NoNextEditReason.Unexpected321;322323export class StatelessNextEditResult {324public static noEdit(reason: NoNextEditReason, telemetryBuilder: StatelessNextEditTelemetryBuilder): StatelessNextEditResult {325const result = Result.error(reason);326const telemetry = telemetryBuilder.build(result);327return new StatelessNextEditResult(result, telemetry);328}329330public static streaming(telemetryBuilder: StatelessNextEditTelemetryBuilder): StatelessNextEditResult {331const result = Result.ok<void>(undefined);332const telemetry = telemetryBuilder.build(result);333return new StatelessNextEditResult(result, telemetry);334}335336constructor(337public readonly nextEdit: Result<void, NoNextEditReason>,338public readonly telemetry: IStatelessNextEditTelemetry,339) {340}341}342343export interface IStatelessNextEditTelemetry {344345readonly hadStatelessNextEditProviderCall: boolean;346347/* general info */348readonly statelessNextEditProviderDuration: number;349readonly isCursorAtEndOfLine: boolean | undefined;350readonly isInlineSuggestion: boolean | undefined;351readonly nLinesOfCurrentFileInPrompt: number | undefined;352readonly modelName: string | undefined;353354/* options info */355readonly logProbThreshold: number | undefined;356357/* prompt info */358359readonly prompt: string | undefined;360readonly promptLineCount: number | undefined;361readonly promptCharCount: number | undefined;362readonly mergeConflictExpanded: 'normal' | 'only' | undefined;363364/* fetch request info */365366readonly debounceTime: number | undefined;367/** This's only used to compute time from inline edit provider call to fetch init. Not included in telemetry. */368readonly fetchStartedAt: number | undefined;369370/* response info */371372/** Artificial delay (aka backoff) on the response based on previous user acceptance/rejection in milliseconds */373readonly artificialDelay: number | undefined;374375readonly hadLowLogProbSuggestion: boolean | undefined;376readonly response: undefined | Promise<FetchResultWithStats>;377378/* suggestions info */379380readonly nEditsSuggested: number | undefined;381readonly lineDistanceToMostRecentEdit: number | undefined;382383/* result info */384readonly nextEditLogprob: number | undefined;385readonly noNextEditReasonKind: string | undefined;386readonly noNextEditReasonMessage: string | undefined;387388/* next cursor line info */389readonly nextCursorPrediction: {390nextCursorLineError: string | undefined;391/** nextCursorLineNumber - currentCursorLineNumber */392nextCursorLineDistance: number | undefined;393isCrossFile: boolean | undefined;394};395396/* xtab aggressiveness telemetry (only set when promptingStrategy is aggressiveness-based) */397readonly xtabAggressivenessLevel: string | undefined;398readonly xtabUserHappinessScore: number | undefined;399400/** The raw user-facing aggressiveness setting value (only set when user changed from default) */401readonly userAggressivenessSetting: string | undefined;402403/* edit intent telemetry (only set when promptingStrategy is Xtab275EditIntent or Xtab275EditIntentShort) */404readonly editIntent: string | undefined;405readonly editIntentParseError: string | undefined;406407/* cursor jump info */408readonly cursorJumpModelName: string | undefined;409readonly cursorJumpPrompt: string | undefined;410readonly cursorJumpResponse: string | undefined;411412/* diff history info */413readonly nDiffsInPrompt: number | undefined;414readonly diffTokensInPrompt: number | undefined;415416/* neighbor (similar files) snippets info */417readonly nNeighborSnippetsComputed: number | undefined;418readonly nNeighborSnippetsInPrompt: number | undefined;419/** JSON-encoded array of original input indices of snippets included in the prompt. */420readonly neighborSnippetIndicesInPrompt: string | undefined;421422/* lint errors info */423readonly lintErrors: string | undefined;424425/* terminal output info */426readonly terminalOutput: string | undefined;427428/* similar files context for telemetry (GhostText-style neighbor code snippets) */429readonly similarFilesContext: Promise<string | undefined> | undefined;430431/* JSON-encoded model configuration from the model service */432readonly modelConfig: string | undefined;433}434435export type FetchResultWithStats = {436readonly ttft: number | undefined;437readonly response: FetchResponse<string>;438readonly fetchTime: number;439readonly fetchResult: ChatFetchResponseType;440};441442export class StatelessNextEditTelemetryBuilder {443444public readonly startTime: number;445public readonly requestUuid: string;446447/**448* It takes a request to automatically capture some properties from the request.449*/450constructor(headerRequestId: string) {451this.startTime = Date.now();452this.requestUuid = headerRequestId;453}454455public build(result: Result<void, NoNextEditReason>): IStatelessNextEditTelemetry {456const endTime = Date.now();457const timeSpent = endTime - this.startTime;458459const prompt = this._prompt ? JSON.stringify(this._prompt.map(({ role, content }) => ({ role, content }))) : undefined;460const promptText = this._prompt ? stringifyChatMessages(this._prompt) : undefined;461const promptLineCount = promptText?.split('\n').length;462const promptCharCount = promptText?.length;463464const noNextEditReasonKind = result.isOk() ? undefined : result.err.kind;465466let noNextEditReasonMessage: string | undefined;467if (result.isError()) {468if (result.err instanceof NoNextEditReason.ActiveDocumentHasNoEdits || result.err instanceof NoNextEditReason.NoSuggestions) {469// ignore470} else if (result.err instanceof NoNextEditReason.GotCancelled || result.err instanceof NoNextEditReason.FilteredOut || result.err instanceof NoNextEditReason.PromptTooLarge) {471noNextEditReasonMessage = result.err.message;472} else if (result.err instanceof NoNextEditReason.FetchFailure || result.err instanceof NoNextEditReason.Uncategorized || result.err instanceof NoNextEditReason.Unexpected) {473noNextEditReasonMessage = result.err.error.stack ? result.err.error.stack : result.err.error.message;474} else {475assertNever(result.err);476}477}478479return {480hadStatelessNextEditProviderCall: true,481482noNextEditReasonKind,483noNextEditReasonMessage,484485statelessNextEditProviderDuration: timeSpent,486logProbThreshold: this._logProbThreshold,487mergeConflictExpanded: this._mergeConflictExpanded,488nLinesOfCurrentFileInPrompt: this._nLinesOfCurrentFileInPrompt,489modelName: this._modelName,490prompt,491promptLineCount,492promptCharCount,493isCursorAtEndOfLine: this._isCursorAtLineEnd,494isInlineSuggestion: this._isInlineSuggestion,495debounceTime: this._debounceTime,496artificialDelay: this._artificialDelay,497fetchStartedAt: this._fetchStartedAt,498hadLowLogProbSuggestion: this._hadLowLogProbSuggestion,499response: this._response,500nEditsSuggested: this._nEditsSuggested,501nextEditLogprob: this._nextEditLogProb,502nextCursorPrediction: this._nextCursorPrediction,503lineDistanceToMostRecentEdit: this._lineDistanceToMostRecentEdit,504xtabAggressivenessLevel: this._xtabAggressivenessLevel,505xtabUserHappinessScore: this._xtabUserHappinessScore,506userAggressivenessSetting: this._userAggressivenessSetting,507editIntent: this._editIntent,508editIntentParseError: this._editIntentParseError,509cursorJumpModelName: this._cursorJumpModelName,510cursorJumpPrompt: this._cursorJumpPrompt ? JSON.stringify(this._cursorJumpPrompt.map(({ role, content }) => ({ role, content }))) : undefined,511cursorJumpResponse: this._cursorJumpResponse,512nDiffsInPrompt: this._nDiffsInPrompt,513diffTokensInPrompt: this._diffTokensInPrompt,514nNeighborSnippetsComputed: this._nNeighborSnippetsComputed,515nNeighborSnippetsInPrompt: this._nNeighborSnippetsInPrompt,516neighborSnippetIndicesInPrompt: this._neighborSnippetIndicesInPrompt,517lintErrors: this._lintErrors,518terminalOutput: this._terminalOutput,519similarFilesContext: this._similarFilesContext,520modelConfig: this._modelConfig,521};522}523524private _logProbThreshold: number | undefined;525public setLogProbThreshold(logProbThreshold: number): this {526this._logProbThreshold = logProbThreshold;527return this;528}529530private _mergeConflictExpanded: 'normal' | 'only' | undefined;531public setMergeConflictExpanded(mergeConflictExpanded: 'normal' | 'only'): this {532this._mergeConflictExpanded = mergeConflictExpanded;533return this;534}535536private _hadLowLogProbSuggestion: boolean | undefined;537public setHadLowLogProbSuggestion(hadLowLogProbSuggestions: boolean): this {538this._hadLowLogProbSuggestion = hadLowLogProbSuggestions;539return this;540}541542private _nLinesOfCurrentFileInPrompt: number | undefined;543public setNLinesOfCurrentFileInPrompt(nLines: number): this {544this._nLinesOfCurrentFileInPrompt = nLines;545return this;546}547548private _modelName: string | undefined;549public setModelName(modelName: string): this {550this._modelName = modelName;551return this;552}553554private _prompt: Raw.ChatMessage[] | undefined;555public setPrompt(prompt: Raw.ChatMessage[]): this {556this._prompt = prompt;557return this;558}559560private _isCursorAtLineEnd: boolean | undefined;561public setIsCursorAtLineEnd(isCursorAtLineEnd: boolean): this {562this._isCursorAtLineEnd = isCursorAtLineEnd;563return this;564}565566private _isInlineSuggestion: boolean | undefined;567public setIsInlineSuggestion(isInlineSuggestion: boolean): this {568this._isInlineSuggestion = isInlineSuggestion;569return this;570}571572private _debounceTime: number | undefined;573public setDebounceTime(debounceTime: number): this {574this._debounceTime = debounceTime;575return this;576}577578private _artificialDelay: number | undefined;579public setArtificialDelay(artificialDelay: number): this {580this._artificialDelay = artificialDelay;581return this;582}583584private _fetchStartedAt: number | undefined;585public setFetchStartedAt(): this {586this._fetchStartedAt = Date.now();587return this;588}589public get fetchStartedAt(): number | undefined {590return this._fetchStartedAt;591}592593private _response: Promise<FetchResultWithStats> | undefined;594public setResponse(response: Promise<{ ttft: number | undefined; response: FetchResponse<string> }>): this {595this._response = response.then(({ response, ttft }) => {596597const fetchTime = Date.now() - this._fetchStartedAt!;598599const fetchResult = response.type;600601return {602ttft,603response,604fetchTime,605fetchResult,606};607});608609return this;610}611612private _cursorJumpModelName: string | undefined;613public setCursorJumpModelName(modelName: string | undefined): this {614this._cursorJumpModelName = modelName;615return this;616}617618private _cursorJumpPrompt: Raw.ChatMessage[] | undefined;619public setCursorJumpPrompt(prompt: Raw.ChatMessage[] | undefined): this {620this._cursorJumpPrompt = prompt;621return this;622}623624private _cursorJumpResponse: string | undefined;625public setCursorJumpResponse(response: string | undefined): this {626this._cursorJumpResponse = response;627return this;628}629630private _nextEditLogProb: number | undefined;631public setNextEditLogProb(logProb: number): this {632this._nextEditLogProb = logProb;633return this;634}635636private _nEditsSuggested: number | undefined;637public setNEditsSuggested(nEditsSuggested: number): this {638this._nEditsSuggested = nEditsSuggested;639return this;640}641642private _lineDistanceToMostRecentEdit: number | undefined;643public setLineDistanceToMostRecentEdit(distanceToMostRecentEdit: number): this {644this._lineDistanceToMostRecentEdit = distanceToMostRecentEdit;645return this;646}647648private _nextCursorPrediction: IStatelessNextEditTelemetry['nextCursorPrediction'] = {649nextCursorLineError: undefined,650nextCursorLineDistance: undefined,651isCrossFile: undefined652};653654public setNextCursorLineError(error: string): this {655this._nextCursorPrediction.nextCursorLineError = error;656return this;657}658659/**660* nextCursorLineNumber - currentCursorLineNumber661*/662public setNextCursorLineDistance(distance: number): this {663this._nextCursorPrediction.nextCursorLineDistance = distance;664return this;665}666667public setNextCursorIsCrossFile(isCrossFile: boolean): this {668this._nextCursorPrediction.isCrossFile = isCrossFile;669return this;670}671672private _xtabAggressivenessLevel: string | undefined;673public setXtabAggressivenessLevel(level: string): this {674this._xtabAggressivenessLevel = level;675return this;676}677678private _xtabUserHappinessScore: number | undefined;679public setXtabUserHappinessScore(score: number): this {680this._xtabUserHappinessScore = score;681return this;682}683684private _userAggressivenessSetting: string | undefined;685public setUserAggressivenessSetting(setting: string): this {686this._userAggressivenessSetting = setting;687return this;688}689690private _editIntent: string | undefined;691public setEditIntent(editIntent: string): this {692this._editIntent = editIntent;693return this;694}695696private _editIntentParseError: string | undefined;697public setEditIntentParseError(error: string): this {698this._editIntentParseError = error;699return this;700}701702private _nDiffsInPrompt: number | undefined;703public setNDiffsInPrompt(n: number): this {704this._nDiffsInPrompt = n;705return this;706}707708private _diffTokensInPrompt: number | undefined;709public setDiffTokensInPrompt(n: number): this {710this._diffTokensInPrompt = n;711return this;712}713714private _nNeighborSnippetsComputed: number | undefined;715public setNNeighborSnippetsComputed(n: number): this {716this._nNeighborSnippetsComputed = n;717return this;718}719720private _nNeighborSnippetsInPrompt: number | undefined;721public setNNeighborSnippetsInPrompt(n: number): this {722this._nNeighborSnippetsInPrompt = n;723return this;724}725726private _neighborSnippetIndicesInPrompt: string | undefined;727public setNeighborSnippetIndicesInPrompt(indices: readonly number[]): this {728this._neighborSnippetIndicesInPrompt = JSON.stringify(indices);729return this;730}731732private _lintErrors: string | undefined;733public setLintErrors(lintErrors: string): this {734this._lintErrors = lintErrors;735return this;736}737738private _terminalOutput: string | undefined;739public setTerminalOutput(terminalOutput: string): this {740this._terminalOutput = terminalOutput;741return this;742}743744private _similarFilesContext: Promise<string | undefined> | undefined;745public setSimilarFilesContext(similarFilesContext: Promise<string | undefined>): this {746this._similarFilesContext = similarFilesContext;747return this;748}749750private _modelConfig: string | undefined;751public setModelConfig(modelConfig: string): this {752this._modelConfig = modelConfig;753return this;754}755}756757758