Skip to content
Merged
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 @@ -5,6 +5,7 @@

import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';
import { AggressivenessLevel, AggressivenessSetting, DEFAULT_USER_HAPPINESS_SCORE_CONFIGURATION, parseUserHappinessScoreConfigurationString, UserHappinessScoreConfiguration } from '../../../platform/inlineEdits/common/dataTypes/xtabPromptOptions';
import { IInlineEditsModelService } from '../../../platform/inlineEdits/common/inlineEditsModelService';
import { ILogService } from '../../../platform/log/common/logService';
import { IExperimentationService } from '../../../platform/telemetry/common/nullExperimentationService';
import { ITelemetryService } from '../../../platform/telemetry/common/telemetry';
Expand Down Expand Up @@ -174,6 +175,7 @@ export class UserInteractionMonitor {
@IExperimentationService private readonly _experimentationService: IExperimentationService,
@ILogService private readonly _logService: ILogService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IInlineEditsModelService private readonly _modelService: IInlineEditsModelService,
) { }

// Capture user interactions
Expand Down Expand Up @@ -217,7 +219,7 @@ export class UserInteractionMonitor {
// Creates a DelaySession based on recent user interactions

public createDelaySession(requestTime: number | undefined): DelaySession {
const baseDebounceTime = this._configurationService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsDebounce, this._experimentationService);
const baseDebounceTime = this._modelService.selectedModelConfiguration().debounce ?? this._configurationService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsDebounce, this._experimentationService);

const backoffDebounceEnabled = this._configurationService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsBackoffDebounceEnabled, this._experimentationService);
const expectedTotalTime = backoffDebounceEnabled ? this._getExpectedTotalTime(baseDebounceTime) : undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1136,8 +1136,9 @@ export class NextEditProvider extends Disposable implements INextEditProvider<Ne
return 0;
}

const cacheDelay = this._configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsCacheDelay, this._expService);
const rebasedCacheDelay = this._configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsRebasedCacheDelay, this._expService);
const modelConfig = this._modelService.selectedModelConfiguration();
const cacheDelay = modelConfig.cacheDelay ?? this._configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsCacheDelay, this._expService);
const rebasedCacheDelay = modelConfig.rebasedCacheDelay ?? this._configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsRebasedCacheDelay, this._expService);
const subsequentCacheDelay = this._configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsSubsequentCacheDelay, this._expService);
const speculativeRequestDelay = this._configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsSpeculativeRequestDelay, this._expService);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ConfigKey, ExperimentBasedConfig, ExperimentBasedConfigType } from '../
import { DefaultsOnlyConfigurationService } from '../../../../platform/configuration/common/defaultsOnlyConfigurationService';
import { InMemoryConfigurationService } from '../../../../platform/configuration/test/common/inMemoryConfigurationService';
import { AggressivenessLevel, AggressivenessSetting, DEFAULT_USER_HAPPINESS_SCORE_CONFIGURATION, UserHappinessScoreConfiguration } from '../../../../platform/inlineEdits/common/dataTypes/xtabPromptOptions';
import { NullInlineEditsModelService } from '../../../../platform/inlineEdits/common/inlineEditsModelService';
import { ILogService } from '../../../../platform/log/common/logService';
import { IExperimentationService, NullExperimentationService } from '../../../../platform/telemetry/common/nullExperimentationService';
import { NullTelemetryService } from '../../../../platform/telemetry/common/nullTelemetryService';
Expand Down Expand Up @@ -98,7 +99,7 @@ describe('UserInteractionMonitor', () => {
experimentationService = new NullExperimentationService();
logService = new TestLogService();
telemetryService = new NullTelemetryService();
monitor = new TestUserInteractionMonitor(configurationService, experimentationService, logService, telemetryService);
monitor = new TestUserInteractionMonitor(configurationService, experimentationService, logService, telemetryService, new NullInlineEditsModelService());
});

describe('history logging', () => {
Expand Down Expand Up @@ -277,7 +278,7 @@ describe('UserInteractionMonitor', () => {
const levelRejectionsRecent = monitor.getAggressivenessLevel().aggressivenessLevel;

// Reset and do opposite order
monitor = new TestUserInteractionMonitor(configurationService, experimentationService, logService, telemetryService);
monitor = new TestUserInteractionMonitor(configurationService, experimentationService, logService, telemetryService, new NullInlineEditsModelService());
for (let i = 0; i < 5; i++) {
monitor.handleRejection();
}
Expand Down Expand Up @@ -356,7 +357,7 @@ describe('UserInteractionMonitor', () => {
beforeEach(() => {
configurationService.useAdaptiveAggressiveness();
mockTelemetryService = new MockTelemetryService();
monitor = new TestUserInteractionMonitor(configurationService, experimentationService, logService, mockTelemetryService);
monitor = new TestUserInteractionMonitor(configurationService, experimentationService, logService, mockTelemetryService, new NullInlineEditsModelService());
});

test('emits telemetry event when config is invalid JSON', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ export class InlineCompletionProviderImpl extends Disposable implements InlineCo

const isCompletionsEnabled = this._isCompletionsEnabled(document);

const unification = this._configurationService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsUnification, this._expService);
const unification = this._modelService.selectedModelConfiguration().supportsUnifiedCompletions
?? this._configurationService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsUnification, this._expService);

const isInlineEditsEnabled = this._configurationService.getExperimentBasedConfig(ConfigKey.InlineEditsEnabled, this._expService, { languageId: document.languageId });

Expand Down Expand Up @@ -467,7 +468,7 @@ export class InlineCompletionProviderImpl extends Disposable implements InlineCo
// re-surface in any other form. Suppress here without evicting the cache entry —
// when the cursor returns to an inline-renderable position, we'll serve it again.
if (
this._nesMimicGhostTextBehavior.get()
(this._modelService.selectedModelConfiguration().nesMimicGhostTextBehavior ?? this._nesMimicGhostTextBehavior.get())
&& !isInlineCompletion
&& isLlmCompletionInfo(suggestionInfo)
&& suggestionInfo.suggestion.result?.cacheEntry?.wasRenderedAsInlineSuggestion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { IEnvService } from '../../../platform/env/common/envService';
import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';
import { JointCompletionsProviderStrategy, JointCompletionsProviderTriggerChangeStrategy } from '../../../platform/inlineEdits/common/dataTypes/jointCompletionsProviderOptions';
import { InlineEditRequestLogContext } from '../../../platform/inlineEdits/common/inlineEditLogContext';
import { IInlineEditsModelService } from '../../../platform/inlineEdits/common/inlineEditsModelService';
import { ObservableGit } from '../../../platform/inlineEdits/common/observableGit';
import { checkIfCursorAtEndOfLine, shortenOpportunityId } from '../../../platform/inlineEdits/common/utils/utils';
import { NesHistoryContextProvider } from '../../../platform/inlineEdits/common/workspaceEditTracker/nesHistoryContextProvider';
Expand Down Expand Up @@ -65,6 +66,7 @@ export class JointCompletionsProviderContribution extends Disposable implements
// private readonly _yieldToCopilot = this._configurationService.getExperimentBasedConfigObservable(ConfigKey.TeamInternal.InlineEditsYieldToCopilot, this._expService);
private readonly _excludedProviders = this._configurationService.getExperimentBasedConfigObservable(ConfigKey.TeamInternal.InlineEditsExcludedProviders, this._expService).map(v => v ? v.split(',').map(v => v.trim()).filter(v => v !== '') : []);
private readonly _copilotToken = observableFromEvent(this, this._authenticationService.onDidCopilotTokenChange, () => this._authenticationService.copilotToken);
private readonly _supportsUnifiedCompletions = observableFromEvent(this, this._modelService.onModelListUpdated, () => this._modelService.selectedModelConfiguration().supportsUnifiedCompletions ?? false);

public readonly inlineEditsEnabled = derived(this, (reader) => {
const copilotToken = this._copilotToken.read(reader);
Expand Down Expand Up @@ -96,6 +98,7 @@ export class JointCompletionsProviderContribution extends Disposable implements
@IExperimentationService private readonly _expService: IExperimentationService,
@IAuthenticationService private readonly _authenticationService: IAuthenticationService,
@IEnvService private readonly _envService: IEnvService,
@IInlineEditsModelService private readonly _modelService: IInlineEditsModelService,
) {
super();

Expand All @@ -117,6 +120,11 @@ export class JointCompletionsProviderContribution extends Disposable implements
reader.store.add(autorun((reader) => {
const unificationStateValue = unificationState.read(reader);

// A model whose strategy bakes in `supportsUnifiedCompletions` runs as the single unified
// provider: this stands in for the `modelUnification` deployment toggle so the behavior can
// be driven purely from the selected model's prompting strategy.
const modelUnification = this._supportsUnifiedCompletions.read(reader) || (unificationStateValue?.modelUnification ?? false);

const excludes = this._excludedProviders.read(reader).slice();

let inlineEditProvider: InlineCompletionProviderImpl | undefined = undefined;
Expand Down Expand Up @@ -211,7 +219,6 @@ export class JointCompletionsProviderContribution extends Disposable implements
const isExcluded = excludes.includes(JointCompletionsProviderContribution.COMPLETIONS_GROUP_ID) && this.inlineEditsEnabled.read(reader);

// @ulugbekna: note that we don't want it if modelUnification is on
const modelUnification = unificationStateValue?.modelUnification ?? false;
if (
(!modelUnification || unificationStateValue?.codeUnification || extensionUnification || configEnabled || this._copilotToken.read(reader)?.isNoAuthUser) &&
!isExcluded
Expand All @@ -229,7 +236,7 @@ export class JointCompletionsProviderContribution extends Disposable implements

const singularProvider = reader.store.add(this._instantiationService.createInstance(JointCompletionsProvider, completionsProvider, inlineEditProvider));

if (unificationStateValue?.modelUnification) {
if (modelUnification) {
if (!excludes.includes('github.copilot')) {
excludes.push('github.copilot');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export function getUserPrompt(promptPieces: PromptPieces): UserPromptResult {
break;
case PromptingStrategy.PatchBased02:
case PromptingStrategy.PatchBased02WithRecentLineNumbers:
case PromptingStrategy.PatchBased02Optimized:
case PromptingStrategy.PatchBased02WithoutRecentLineNumbers: {
const currentDocument = promptPieces.currentDocument;
const cursorLine = currentDocument.lineWithCursor();
Expand Down Expand Up @@ -175,6 +176,7 @@ export function getUserPrompt(promptPieces: PromptPieces): UserPromptResult {
opts.promptingStrategy !== PromptingStrategy.PatchBased01 &&
opts.promptingStrategy !== PromptingStrategy.PatchBased02 &&
opts.promptingStrategy !== PromptingStrategy.PatchBased02WithRecentLineNumbers &&
opts.promptingStrategy !== PromptingStrategy.PatchBased02Optimized &&
opts.promptingStrategy !== PromptingStrategy.PatchBased02WithoutRecentLineNumbers;

const packagedPrompt = includeBackticks ? wrapInBackticks(mainPrompt) : mainPrompt;
Expand Down Expand Up @@ -379,6 +381,7 @@ function getPostScript(options: PromptOptions, currentFilePath: string, aggressi
break;
case PromptingStrategy.PatchBased02:
case PromptingStrategy.PatchBased02WithRecentLineNumbers:
case PromptingStrategy.PatchBased02Optimized:
case PromptingStrategy.PatchBased02WithoutRecentLineNumbers:
postScript = eagernessPrompt === 'aggressionHighLow'
? aggressivenessLevel === AggressivenessLevel.Medium
Expand Down
16 changes: 9 additions & 7 deletions extensions/copilot/src/extension/xtab/node/xtabProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ export class XtabProvider implements IStatelessNextEditProvider {

const currentDocument = new CurrentDocument(activeDocument.documentAfterEdits, cursorPosition);

this._configureDebounceTimings(request, currentDocument, promptOptions, telemetry, delaySession, tracer);
this._configureDebounceTimings(request, currentDocument, promptOptions, modelServiceConfig, telemetry, delaySession, tracer);

const areaAroundEditWindowLinesRange = computeAreaAroundEditWindowLinesRange(currentDocument);

Expand Down Expand Up @@ -507,7 +507,7 @@ export class XtabProvider implements IStatelessNextEditProvider {

const responseFormat = xtabPromptOptions.ResponseFormat.fromPromptingStrategy(promptOptions.promptingStrategy);

const prediction = this.getPredictedOutput(activeDocument, currentDocument.cursorLineOffset, editWindowLines, cursorLineInEditWindowOffset, responseFormat);
const prediction = this.getPredictedOutput(activeDocument, currentDocument.cursorLineOffset, editWindowLines, cursorLineInEditWindowOffset, responseFormat, modelServiceConfig);

const systemMsg = pickSystemPrompt(promptOptions.promptingStrategy);
const messages = constructMessages({
Expand Down Expand Up @@ -589,6 +589,7 @@ export class XtabProvider implements IStatelessNextEditProvider {
request: StatelessNextEditRequest,
currentDocument: CurrentDocument,
promptOptions: ModelConfig,
modelServiceConfig: xtabPromptOptions.ModelConfiguration,
telemetry: StatelessNextEditTelemetryBuilder,
delaySession: DelaySession,
tracer: ILogger,
Expand All @@ -610,7 +611,7 @@ export class XtabProvider implements IStatelessNextEditProvider {
delaySession.setExtraDebounce(inlineSuggestionDebounce);
} else if (isCursorAtEndOfLine) {
tracer.trace('Debouncing for cursor at end of line');
delaySession.setExtraDebounce(this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsExtraDebounceEndOfLine, this.expService));
delaySession.setExtraDebounce(modelServiceConfig.extraDebounceEndOfLine ?? this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsExtraDebounceEndOfLine, this.expService));
} else {
tracer.trace('No extra debounce applied');
}
Expand Down Expand Up @@ -1057,9 +1058,9 @@ export class XtabProvider implements IStatelessNextEditProvider {
const lastLineLength = lastLine.length;
const pseudoEditWindow = currentDocument.transformer.getOffsetRange(new Range(clippedTaggedCurrentDoc.keptRange.start + 1, 1, keptRangeEndExclusive, lastLineLength + 1));
const duplicateAdditionsMode = this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabDuplicateAdditionsMode, this.expService);
const fastYieldLineWithCursor = this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabProviderPatchFastYieldLineWithCursor, this.expService);
const fastYieldLineWithCursor = editStreamCtx.modelServiceConfig.patchFastYieldLineWithCursor ?? this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabProviderPatchFastYieldLineWithCursor, this.expService);
const fastYieldLineWithCursorMultiLine = this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabProviderPatchFastYieldLineWithCursorMultiLine, this.expService);
const splitPatchOnDiff = this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabSplitPatchOnDiff, this.expService);
const splitPatchOnDiff = editStreamCtx.modelServiceConfig.splitPatchOnDiff ?? this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabSplitPatchOnDiff, this.expService);
parseResult = new ResponseParseResult.DirectEdits(
XtabPatchResponseHandler.handleResponse(
linesStream,
Expand Down Expand Up @@ -1636,14 +1637,14 @@ export class XtabProvider implements IStatelessNextEditProvider {
return createProxyXtabEndpoint(this.instaService, configuredModelName);
}

private getPredictedOutput(doc: StatelessNextEditDocument, cursorLineOffset: number, editWindowLines: string[], cursorLineInEditWindowOffset: number, responseFormat: xtabPromptOptions.ResponseFormat): Prediction | undefined {
private getPredictedOutput(doc: StatelessNextEditDocument, cursorLineOffset: number, editWindowLines: string[], cursorLineInEditWindowOffset: number, responseFormat: xtabPromptOptions.ResponseFormat, modelServiceConfig: xtabPromptOptions.ModelConfiguration): Prediction | undefined {
const usePrediction = this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabProviderUsePrediction, this.expService);
if (!usePrediction) {
return undefined;
}
// Only the CustomDiffPatch shape consults `patchModelPredictionKind`; skip the experiment lookup otherwise.
const patchModelPredictionKind = responseFormat === xtabPromptOptions.ResponseFormat.CustomDiffPatch
? this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabProviderPatchModelPredictionKind, this.expService)
? modelServiceConfig.patchModelPredictionKind ?? this.configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsXtabProviderPatchModelPredictionKind, this.expService)
: xtabPromptOptions.PatchModelPrediction.FilePath;
return {
type: 'content',
Expand Down Expand Up @@ -1819,6 +1820,7 @@ export function pickSystemPrompt(promptingStrategy: xtabPromptOptions.PromptingS
case xtabPromptOptions.PromptingStrategy.PatchBased01:
case xtabPromptOptions.PromptingStrategy.PatchBased02:
case xtabPromptOptions.PromptingStrategy.PatchBased02WithRecentLineNumbers:
case xtabPromptOptions.PromptingStrategy.PatchBased02Optimized:
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 @@ -167,6 +167,7 @@ describe('pickSystemPrompt', () => {
PromptingStrategy.PatchBased01,
PromptingStrategy.PatchBased02,
PromptingStrategy.PatchBased02WithRecentLineNumbers,
PromptingStrategy.PatchBased02Optimized,
PromptingStrategy.PatchBased02WithoutRecentLineNumbers,
PromptingStrategy.Xtab275,
PromptingStrategy.XtabAggressiveness,
Expand Down
Loading
Loading