Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export function getUserPrompt(promptPieces: PromptPieces): UserPromptResult {
case PromptingStrategy.PatchBased02:
case PromptingStrategy.PatchBased02WithRecentLineNumbers:
case PromptingStrategy.PatchBased02Optimized:
case PromptingStrategy.PatchBased02OptimizedFourInOne:
case PromptingStrategy.PatchBased02WithoutRecentLineNumbers: {
const currentDocument = promptPieces.currentDocument;
const cursorLine = currentDocument.lineWithCursor();
Expand Down Expand Up @@ -177,6 +178,7 @@ export function getUserPrompt(promptPieces: PromptPieces): UserPromptResult {
opts.promptingStrategy !== PromptingStrategy.PatchBased02 &&
opts.promptingStrategy !== PromptingStrategy.PatchBased02WithRecentLineNumbers &&
opts.promptingStrategy !== PromptingStrategy.PatchBased02Optimized &&
opts.promptingStrategy !== PromptingStrategy.PatchBased02OptimizedFourInOne &&
opts.promptingStrategy !== PromptingStrategy.PatchBased02WithoutRecentLineNumbers;

const packagedPrompt = includeBackticks ? wrapInBackticks(mainPrompt) : mainPrompt;
Expand Down Expand Up @@ -382,6 +384,7 @@ function getPostScript(options: PromptOptions, currentFilePath: string, aggressi
case PromptingStrategy.PatchBased02:
case PromptingStrategy.PatchBased02WithRecentLineNumbers:
case PromptingStrategy.PatchBased02Optimized:
case PromptingStrategy.PatchBased02OptimizedFourInOne:
case PromptingStrategy.PatchBased02WithoutRecentLineNumbers:
postScript = eagernessPrompt === 'aggressionHighLow'
? aggressivenessLevel === AggressivenessLevel.Medium
Expand Down
3 changes: 2 additions & 1 deletion extensions/copilot/src/extension/xtab/node/xtabProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1525,7 +1525,7 @@ export class XtabProvider implements IStatelessNextEditProvider {
modelName: undefined,
...xtabPromptOptions.DEFAULT_OPTIONS,
};
const defaultModelConfig = this.modelService.defaultModelConfiguration();
const defaultModelConfig = xtabPromptOptions.applyStrategyConfig(this.modelService.defaultModelConfiguration());
return {
promptOptions: overrideModelConfig(defaultOptions, defaultModelConfig),
modelServiceConfig: defaultModelConfig
Expand Down Expand Up @@ -1821,6 +1821,7 @@ export function pickSystemPrompt(promptingStrategy: xtabPromptOptions.PromptingS
case xtabPromptOptions.PromptingStrategy.PatchBased02:
case xtabPromptOptions.PromptingStrategy.PatchBased02WithRecentLineNumbers:
case xtabPromptOptions.PromptingStrategy.PatchBased02Optimized:
case xtabPromptOptions.PromptingStrategy.PatchBased02OptimizedFourInOne:
case xtabPromptOptions.PromptingStrategy.PatchBased02WithoutRecentLineNumbers:
case xtabPromptOptions.PromptingStrategy.Xtab275:
case xtabPromptOptions.PromptingStrategy.XtabAggressiveness:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,32 @@ describe('getUserPrompt', () => {
}
});

test.each([
[AggressivenessLevel.Medium, ''],
[AggressivenessLevel.High, '<|aggression|>high<|/aggression|>'],
[AggressivenessLevel.Low, '<|aggression|>low<|/aggression|>'],
])('PatchBased02OptimizedFourInOne places the %s eagerness tag before the postscript', (aggressivenessLevel, eagernessTag) => {
const pieces = createTestPromptPieces({
cursorLine: 2,
cursorColumn: 9,
strategy: PromptingStrategy.PatchBased02OptimizedFourInOne,
eagernessPrompt: 'aggressionHighLow',
aggressivenessLevel,
});
const { prompt } = getUserPrompt(pieces);

const cursorLocation = `${PromptTags.CURSOR_LOCATION.start}\n const ${PromptTags.CURSOR}x = 1;\n${PromptTags.CURSOR_LOCATION.end}`;
const postScript = 'The developer was working on a section of code within the `current_file_content`';
expect(prompt).toContain(cursorLocation);
expect(prompt.indexOf(cursorLocation)).toBeLessThan(prompt.indexOf(postScript));
if (aggressivenessLevel === AggressivenessLevel.Medium) {
expect(prompt).not.toContain('<|aggression|>');
expect(prompt).toContain(`${PromptTags.CURSOR_LOCATION.end}\n\n${postScript}`);
} else {
expect(prompt).toContain(`${PromptTags.CURSOR_LOCATION.end}\n\n${eagernessTag}\n\n${postScript}`);
}
});

describe('Xtab275AggressivenessHighLow', () => {
test('medium level does not include aggressive tag', () => {
const pieces = createTestPromptPieces({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { DocumentId } from '../../../../platform/inlineEdits/common/dataTypes/do
import { Edits } from '../../../../platform/inlineEdits/common/dataTypes/edit';
import { ImportChanges } from '../../../../platform/inlineEdits/common/dataTypes/importFilteringOptions';
import { LanguageId } from '../../../../platform/inlineEdits/common/dataTypes/languageId';
import { AggressivenessLevel, DEFAULT_OPTIONS, EarlyDivergenceCancellationMode, LanguageContextLanguages, LintOptionShowCode, LintOptionWarning, ModelConfiguration, PatchModelPrediction, PromptingStrategy, ResponseFormat } from '../../../../platform/inlineEdits/common/dataTypes/xtabPromptOptions';
import { AggressivenessLevel, AggressivenessSetting, DEFAULT_OPTIONS, EarlyDivergenceCancellationMode, LanguageContextLanguages, LintOptionShowCode, LintOptionWarning, ModelConfiguration, PatchModelPrediction, PromptingStrategy, ResponseFormat } from '../../../../platform/inlineEdits/common/dataTypes/xtabPromptOptions';
import { InlineEditRequestLogContext } from '../../../../platform/inlineEdits/common/inlineEditLogContext';
import { IInlineEditsModelService } from '../../../../platform/inlineEdits/common/inlineEditsModelService';
import { NoNextEditReason, StatelessNextEditDocument, StatelessNextEditRequest, StreamedEdit, WithStatelessProviderTelemetry } from '../../../../platform/inlineEdits/common/statelessNextEditProvider';
Expand Down Expand Up @@ -168,6 +168,7 @@ describe('pickSystemPrompt', () => {
PromptingStrategy.PatchBased02,
PromptingStrategy.PatchBased02WithRecentLineNumbers,
PromptingStrategy.PatchBased02Optimized,
PromptingStrategy.PatchBased02OptimizedFourInOne,
PromptingStrategy.PatchBased02WithoutRecentLineNumbers,
PromptingStrategy.Xtab275,
PromptingStrategy.XtabAggressiveness,
Expand Down Expand Up @@ -273,6 +274,18 @@ describe('overrideModelConfig', () => {
expect(result.diffHistory).toEqual(base.diffHistory);
});

it('propagates the eagerness prompt from model configuration', () => {
const result = overrideModelConfig(makeBaseModelConfig(), {
modelName: 'four-in-one-model',
promptingStrategy: PromptingStrategy.PatchBased02OptimizedFourInOne,
eagernessPrompt: 'aggressionHighLow',
includeTagsInCurrentFile: false,
lintOptions: undefined,
});

expect(result.eagernessPrompt).toBe('aggressionHighLow');
});

it('merges lintOptions when overridingConfig has lintOptions', () => {
const testLintOptions = { tagName: 'lint', warnings: LintOptionWarning.YES, showCode: LintOptionShowCode.YES, maxLints: 5, maxLineDistance: 10, nRecentFiles: 0 };
const base: ModelConfig = {
Expand Down Expand Up @@ -1108,6 +1121,35 @@ describe('XtabProvider integration', () => {
expect(streamingFetcher.callCount).toBe(2);
});

it('applies four-in-one strategy config when retrying with the default model', async () => {
const provider = createProvider();
await configService.setConfig(ConfigKey.Advanced.InlineEditsAggressiveness, AggressivenessSetting.High);
mockModelService.setDefaultConfig({
promptingStrategy: PromptingStrategy.PatchBased02OptimizedFourInOne,
});

const lines = ['const x = 1;'];
const request = createRequestWithEdit(lines, { insertionOffset: 3, insertedText: 'a' });
streamingFetcher.enqueueResponse({
type: ChatFetchResponseType.NotFound,
reason: 'test',
requestId: 'req-1',
serverRequestId: undefined,
});
streamingFetcher.setStreamingLines(lines);

const capturesBefore = streamingFetcher.capturedOptions.length;
const gen = provider.provideNextEdit(request, createMockLogger(), createLogContext(), CancellationToken.None);
await AsyncIterUtils.drainUntilReturn(gen);

const retryPrompts = streamingFetcher.capturedOptions
.slice(capturesBefore)
.flatMap(options => options.messages)
.filter(message => message.role === Raw.ChatRole.User)
.map(getMessageText);
expect(retryPrompts.some(prompt => prompt.includes('<|aggression|>'))).toBe(true);
});

it('does not loop infinitely on repeated NotFound', async () => {
const provider = createProvider();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@ export enum PromptingStrategy {
PatchBased02WithRecentLineNumbers = 'patchBased02WithRecentLineNumbers',
/** Optimized PatchBased02 variant with line numbers on recent docs. */
PatchBased02Optimized = 'patchBased02Optimized',
/** Optimized PatchBased02 variant trained for four-in-one eagerness prompting. */
PatchBased02OptimizedFourInOne = 'patchBased02OptimizedFourInOne',
/** PatchBased02 variant: no line numbers on recent docs. */
PatchBased02WithoutRecentLineNumbers = 'patchBased02WithoutRecentLineNumbers',
/**
Expand Down Expand Up @@ -571,6 +573,7 @@ export function isEagernessPrompt(options: PromptOptions): boolean {
return (options.eagernessPrompt !== undefined && [
PromptingStrategy.PatchBased02,
PromptingStrategy.PatchBased02WithRecentLineNumbers,
PromptingStrategy.PatchBased02OptimizedFourInOne,
PromptingStrategy.PatchBased02WithoutRecentLineNumbers,
].includes(options.promptingStrategy)) // eagerness prompt option is only supported for patch-based strategies
|| [PromptingStrategy.XtabAggressiveness,
Expand Down Expand Up @@ -611,6 +614,7 @@ export namespace ResponseFormat {
case PromptingStrategy.PatchBased02:
case PromptingStrategy.PatchBased02WithRecentLineNumbers:
case PromptingStrategy.PatchBased02Optimized:
case PromptingStrategy.PatchBased02OptimizedFourInOne:
case PromptingStrategy.PatchBased02WithoutRecentLineNumbers:
return ResponseFormat.CustomDiffPatch;
case PromptingStrategy.Xtab275EditIntent:
Expand Down Expand Up @@ -828,6 +832,9 @@ const STRATEGY_CONFIG: Partial<Record<PromptingStrategy, Partial<ModelConfigurat
supportsNextCursorLinePrediction: false,
allowImportChanges: ImportChanges.All,
},
[PromptingStrategy.PatchBased02OptimizedFourInOne]: {
eagernessPrompt: 'aggressionHighLow',
},
};

/** Apply per-strategy baked-in config; strategy values override `config`. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ describe('applyStrategyConfig', () => {
expect(result.currentFile?.includeLineNumbers).toBe(IncludeLineNumbersOption.WithoutSpace);
});

it('sets the four-in-one eagerness prompt for PatchBased02OptimizedFourInOne', () => {
const result = applyStrategyConfig(baseConfig({
promptingStrategy: PromptingStrategy.PatchBased02OptimizedFourInOne,
}));
expect(result.eagernessPrompt).toBe('aggressionHighLow');
expect(applyStrategyConfig(baseConfig({
promptingStrategy: PromptingStrategy.PatchBased02Optimized,
})).eagernessPrompt).toBeUndefined();
});

it('preserves undefined for option bags neither side specifies', () => {
const result = applyStrategyConfig(baseConfig({
promptingStrategy: PromptingStrategy.CopilotNesXtab,
Expand Down Expand Up @@ -159,6 +169,19 @@ describe('isEagernessPrompt', () => {
expect(isEagernessPrompt({ ...DEFAULT_OPTIONS, promptingStrategy: PromptingStrategy.PatchBased02, eagernessPrompt: 'aggressionHighLow' })).toBe(true);
expect(isEagernessPrompt({ ...DEFAULT_OPTIONS, promptingStrategy: PromptingStrategy.PatchBased02 })).toBe(false);
});

it('recognizes the standalone four-in-one strategy after strategy config is applied', () => {
const config = applyStrategyConfig(baseConfig({
promptingStrategy: PromptingStrategy.PatchBased02OptimizedFourInOne,
}));
const options = {
...DEFAULT_OPTIONS,
promptingStrategy: config.promptingStrategy,
eagernessPrompt: config.eagernessPrompt,
};
expect(isEagernessPrompt(options)).toBe(true);
expect(isEagernessPrompt(options)).toBe(true);
});
});

describe('GlobalBudgetOptions', () => {
Expand Down
Loading