From d41e63e2b07f9746cd3c7ec9dc281e7917d5ec6b Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Fri, 17 Oct 2025 00:09:21 +0200 Subject: [PATCH] nes: add settings to configure NES model and default NES model using a string setting (#1374) --- .../src/extension/xtab/node/xtabProvider.ts | 70 +++++++++++++++---- .../common/configurationService.ts | 2 + 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/extensions/copilot/src/extension/xtab/node/xtabProvider.ts b/extensions/copilot/src/extension/xtab/node/xtabProvider.ts index b6c39cf32c4..45665b18d0e 100644 --- a/extensions/copilot/src/extension/xtab/node/xtabProvider.ts +++ b/extensions/copilot/src/extension/xtab/node/xtabProvider.ts @@ -9,7 +9,7 @@ import { ChatCompletionContentPartKind } from '@vscode/prompt-tsx/dist/base/outp import { FetchStreamSource } from '../../../platform/chat/common/chatMLFetcher'; import { ChatFetchError, ChatFetchResponseType, ChatLocation } from '../../../platform/chat/common/commonTypes'; import { toTextParts } from '../../../platform/chat/common/globalStringUtils'; -import { ConfigKey, IConfigurationService, XTabProviderId } from '../../../platform/configuration/common/configurationService'; +import { ConfigKey, ExperimentBasedConfig, IConfigurationService, XTabProviderId } from '../../../platform/configuration/common/configurationService'; import { IDiffService } from '../../../platform/diff/common/diffService'; import { ChatEndpoint } from '../../../platform/endpoint/node/chatEndpoint'; import { createProxyXtabEndpoint } from '../../../platform/endpoint/node/proxyXtabEndpoint'; @@ -31,6 +31,7 @@ import { OptionalChatRequestParams, Prediction } from '../../../platform/network import { IChatEndpoint } from '../../../platform/networking/common/networking'; import { ISimulationTestContext } from '../../../platform/simulationTestContext/common/simulationTestContext'; import { IExperimentationService } from '../../../platform/telemetry/common/nullExperimentationService'; +import { ITelemetryService } from '../../../platform/telemetry/common/telemetry'; import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService'; import { raceFilter } from '../../../util/common/async'; import * as errors from '../../../util/common/errors'; @@ -107,6 +108,7 @@ export class XtabProvider implements IStatelessNextEditProvider { @ILanguageContextProviderService private readonly langCtxService: ILanguageContextProviderService, @ILanguageDiagnosticsService private readonly langDiagService: ILanguageDiagnosticsService, @IIgnoreService private readonly ignoreService: IIgnoreService, + @ITelemetryService private readonly telemetryService: ITelemetryService ) { this.delayer = new Delayer(this.configService, this.expService); this.tracer = createTracer(['NES', 'XtabProvider'], (s) => this.logService.trace(s)); @@ -1019,22 +1021,64 @@ export class XtabProvider implements IStatelessNextEditProvider { includePostScript: true, }; - const overridingModelConfig = this.configService.getConfig(ConfigKey.Internal.InlineEditsXtabProviderModelConfiguration); - - if (overridingModelConfig) { - return { - ...sourcedModelConfig, - modelName: overridingModelConfig.modelName, - promptingStrategy: overridingModelConfig.promptingStrategy, - currentFile: { - ...sourcedModelConfig.currentFile, - includeTags: overridingModelConfig.includeTagsInCurrentFile, - }, - }; + const localOverridingModelConfig = this.configService.getConfig(ConfigKey.Internal.InlineEditsXtabProviderModelConfiguration); + if (localOverridingModelConfig) { + return XtabProvider.overrideModelConfig(sourcedModelConfig, localOverridingModelConfig); } + + const expBasedModelConfig = this.overrideByStringModelConfig(sourcedModelConfig, ConfigKey.Internal.InlineEditsXtabProviderModelConfigurationString); + if (expBasedModelConfig) { + return expBasedModelConfig; + } + + const defaultModelConfig = this.overrideByStringModelConfig(sourcedModelConfig, ConfigKey.Internal.InlineEditsXtabProviderDefaultModelConfigurationString); + if (defaultModelConfig) { + return defaultModelConfig; + } + return sourcedModelConfig; } + private overrideByStringModelConfig(originalModelConfig: ModelConfig, configKey: ExperimentBasedConfig): ModelConfig | undefined { + const configString = this.configService.getExperimentBasedConfig(configKey, this.expService); + if (configString === undefined) { + return undefined; + } + + let parsedConfig: xtabPromptOptions.ModelConfiguration | undefined; + try { + parsedConfig = JSON.parse(configString); + } catch (e: unknown) { + /* __GDPR__ + "incorrectNesModelConfig" : { + "owner": "ulugbekna", + "comment": "Capture if model configuration string is invalid JSON.", + "configName": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Name of the configuration that failed to parse." }, + "errorMessage": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Error message from JSON.parse." } + } + */ + this.telemetryService.sendMSFTTelemetryEvent('incorrectNesModelConfig', { configName: configKey.id, errorMessage: errors.toString(errors.fromUnknown(e)) }); + } + + if (parsedConfig) { + return XtabProvider.overrideModelConfig(originalModelConfig, parsedConfig); + } + + return undefined; + } + + private static overrideModelConfig(modelConfig: ModelConfig, overridingConfig: xtabPromptOptions.ModelConfiguration): ModelConfig { + return { + ...modelConfig, + modelName: overridingConfig.modelName, + promptingStrategy: overridingConfig.promptingStrategy, + currentFile: { + ...modelConfig.currentFile, + includeTags: overridingConfig.includeTagsInCurrentFile, + }, + }; + } + private async predictNextCursorPosition(promptPieces: PromptPieces): Promise> { const tracer = this.tracer.sub('predictNextCursorPosition'); diff --git a/extensions/copilot/src/platform/configuration/common/configurationService.ts b/extensions/copilot/src/platform/configuration/common/configurationService.ts index 7e068416002..f3154441af4 100644 --- a/extensions/copilot/src/platform/configuration/common/configurationService.ts +++ b/extensions/copilot/src/platform/configuration/common/configurationService.ts @@ -670,6 +670,8 @@ export namespace ConfigKey { export const InlineEditsXtabProviderUrl = defineValidatedSetting('chat.advanced.inlineEdits.xtabProvider.url', vString(), undefined, INTERNAL_RESTRICTED); export const InlineEditsXtabProviderApiKey = defineValidatedSetting('chat.advanced.inlineEdits.xtabProvider.apiKey', vString(), undefined, INTERNAL_RESTRICTED); export const InlineEditsXtabProviderModelConfiguration = defineValidatedSetting('chat.advanced.inlineEdits.xtabProvider.modelConfiguration', xtabPromptOptions.MODEL_CONFIGURATION_VALIDATOR, { defaultValue: undefined, teamDefaultValue: { modelName: "xtab-281-v2-bs48-2", promptingStrategy: xtabPromptOptions.PromptingStrategy.Xtab275, includeTagsInCurrentFile: false } }, INTERNAL_RESTRICTED); + export const InlineEditsXtabProviderModelConfigurationString = defineExpSetting('chat.advanced.inlineEdits.xtabProvider.modelConfigurationString', undefined, INTERNAL_RESTRICTED); + export const InlineEditsXtabProviderDefaultModelConfigurationString = defineExpSetting('chat.advanced.inlineEdits.xtabProvider.defaultModelConfigurationString', undefined, INTERNAL_RESTRICTED); export const InlineEditsXtabProviderModelName = defineExpSetting('chat.advanced.inlineEdits.xtabProvider.modelName', undefined, INTERNAL_RESTRICTED); export const InlineEditsInlineCompletionsEnabled = defineValidatedSetting('chat.advanced.inlineEdits.inlineCompletions.enabled', vBoolean(), true, INTERNAL_RESTRICTED); export const InlineEditsXtabProviderUsePrediction = defineValidatedSetting('chat.advanced.inlineEdits.xtabProvider.usePrediction', vBoolean(), true, INTERNAL_RESTRICTED);