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
12 changes: 12 additions & 0 deletions extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4008,6 +4008,18 @@
{
"id": "advanced",
"properties": {
"github.copilot.chat.inlineEdits.xtabProvider.modelConfiguration": {
"type": [
"object",
"null"
],
"default": null,
"markdownDescription": "Advanced model configuration for the next edit suggestions xtab provider.\n\n**Note**: This is an advanced setting.",
"tags": [
"advanced",
"experimental"
]
},
"github.copilot.chat.reasoningEffortOverride": {
"type": [
"string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,13 @@ export namespace ConfigKey {

/** Internal: override reasoning/thinking effort sent to model APIs (e.g. Responses API, Messages API). Used by evals. */
export const ReasoningEffortOverride = defineSetting<string | null>('chat.reasoningEffortOverride', ConfigType.Simple, null);

export const InlineEditsXtabProviderModelConfiguration = (() => {
const oldKey = 'chat.advanced.inlineEdits.xtabProvider.modelConfiguration';
const newKey = 'chat.inlineEdits.xtabProvider.modelConfiguration';
migrateSetting(newKey, oldKey);
return defineSetting<xtabPromptOptions.ModelConfiguration | null>(newKey, ConfigType.Simple, null, xtabPromptOptions.MODEL_CONFIGURATION_VALIDATOR, { oldKey });
})();
}

/**
Expand Down Expand Up @@ -749,7 +756,6 @@ export namespace ConfigKey {
export const InlineEditsNextCursorPredictionApiKey = defineTeamInternalSetting<string | undefined>('chat.advanced.inlineEdits.nextCursorPrediction.apiKey', ConfigType.Simple, undefined, vString());
export const InlineEditsXtabProviderUrl = defineTeamInternalSetting<string | undefined>('chat.advanced.inlineEdits.xtabProvider.url', ConfigType.Simple, undefined, vString());
export const InlineEditsXtabProviderApiKey = defineTeamInternalSetting<string | undefined>('chat.advanced.inlineEdits.xtabProvider.apiKey', ConfigType.Simple, undefined, vString());
export const InlineEditsXtabProviderModelConfiguration = defineTeamInternalSetting<xtabPromptOptions.ModelConfiguration | undefined>('chat.advanced.inlineEdits.xtabProvider.modelConfiguration', ConfigType.Simple, undefined, xtabPromptOptions.MODEL_CONFIGURATION_VALIDATOR);
export const InlineEditsNextCursorPredictionLintOptions = defineTeamInternalSetting<Partial<xtabPromptOptions.LintOptions> | undefined>('chat.advanced.inlineEdits.nextCursorPrediction.lintOptions', ConfigType.Simple, undefined, xtabPromptOptions.LINT_OPTIONS_VALIDATOR);
export const InlineEditsInlineCompletionsEnabled = defineTeamInternalSetting<boolean>('chat.advanced.inlineEdits.inlineCompletions.enabled', ConfigType.Simple, true, vBoolean());
export const InlineEditsInlineCompletionsAdvanced = defineTeamInternalSetting<boolean>('chat.advanced.inlineEdits.inlineCompletions.advancedDetection', ConfigType.ExperimentBased, true, vBoolean());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { describe, expect, test, vi } from 'vitest';

const { mockConfigStore } = vi.hoisted(() => ({
mockConfigStore: { user: {} as Record<string, unknown>, defaults: {} as Record<string, unknown> },
}));

vi.mock('vscode', () => {
function makeConfig(prefix: string) {
const fullKey = (k: string) => prefix ? `${prefix}.${k}` : k;
return {
get<T>(k: string): T | undefined {
const fk = fullKey(k);
if (fk in mockConfigStore.user) {
return mockConfigStore.user[fk] as T;
}
if (fk in mockConfigStore.defaults) {
return mockConfigStore.defaults[fk] as T;
}
return undefined;
},
inspect<T>(k: string) {
const fk = fullKey(k);
return {
key: fk,
defaultValue: mockConfigStore.defaults[fk] as T | undefined,
globalValue: (fk in mockConfigStore.user ? mockConfigStore.user[fk] : undefined) as T | undefined,
};
},
};
}
return {
workspace: {
getConfiguration: (prefix: string) => makeConfig(prefix),
onDidChangeConfiguration: () => ({ dispose() { } }),
},
};
});

import { ICopilotTokenStore } from '../../../authentication/common/copilotTokenStore';
import { ConfigKey } from '../../common/configurationService';
import { ConfigurationServiceImpl } from '../configurationServiceImpl';

const fakeTokenStore: ICopilotTokenStore = {
copilotToken: undefined,
onDidStoreUpdate: () => ({ dispose() { } }),
} as any;

describe('ConfigurationServiceImpl - migrated chat.advanced setting fallback', () => {
test('reads the user-set OLD key when only the OLD key is configured', () => {
const oldKey = `github.copilot.${ConfigKey.Advanced.InlineEditsXtabProviderModelConfiguration.oldId}`;
const newKey = ConfigKey.Advanced.InlineEditsXtabProviderModelConfiguration.fullyQualifiedId;

const userValue = {
modelName: 'dd_5minichat_edits_xtab_300_small',
promptingStrategy: 'xtab275',
includeTagsInCurrentFile: false,
};

mockConfigStore.user = { [oldKey]: userValue };
// The new key is registered with `type: ["object", "null"]` and `default: null`,
// so an unconfigured user reads `null` for the new key, allowing the
// `?? config.get(oldKey)` fallback to take over.
mockConfigStore.defaults = { [newKey]: null };

const svc = new ConfigurationServiceImpl(fakeTokenStore);
const value = svc.getConfig(ConfigKey.Advanced.InlineEditsXtabProviderModelConfiguration);

expect(value).toEqual(userValue);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class InlineEditsModelService extends Disposable implements IInlineEditsM
private _fetchedModelsObs = observableFromEvent(this, this._proxyModelsService.onModelListUpdated, () => this._proxyModelsService.nesModels);

private _preferredModelNameObs = this._configService.getExperimentBasedConfigObservable(ConfigKey.Advanced.InlineEditsPreferredModel, this._expService);
private _localModelConfigObs = this._configService.getConfigObservable(ConfigKey.TeamInternal.InlineEditsXtabProviderModelConfiguration);
private _localModelConfigObs = this._configService.getConfigObservable(ConfigKey.Advanced.InlineEditsXtabProviderModelConfiguration);
Comment thread
ulugbekna marked this conversation as resolved.
private _expBasedModelConfigObs = this._configService.getExperimentBasedConfigObservable(ConfigKey.TeamInternal.InlineEditsXtabProviderModelConfigurationString, this._expService);
private _defaultModelConfigObs = this._configService.getExperimentBasedConfigObservable(ConfigKey.TeamInternal.InlineEditsXtabProviderDefaultModelConfigurationString, this._expService);
private _useSlashModelsObs = this._configService.getExperimentBasedConfigObservable(ConfigKey.TeamInternal.InlineEditsUseSlashModels, this._expService);
Expand Down Expand Up @@ -210,7 +210,7 @@ export class InlineEditsModelService extends Disposable implements IInlineEditsM
}: {
copilotToken: CopilotToken | undefined;
fetchedNesModels: WireTypes.Model.t[] | undefined;
localModelConfig: ModelConfiguration | undefined;
localModelConfig: ModelConfiguration | null;
modelConfigString: string | undefined;
defaultModelConfigString: string | undefined;
useSlashModels: boolean;
Expand Down
2 changes: 1 addition & 1 deletion extensions/copilot/test/base/simulationOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ export class SimulationOptions {
``,
`Global options (placed before 'nes-datagen'):`,
` --config-file Path to a JSON config file (required for nes-datagen)`,
` Must include "chat.advanced.inlineEdits.xtabProvider.modelConfiguration"`,
` Must include "github.copilot.chat.inlineEdits.xtabProvider.modelConfiguration"`,
` with at least { "modelName", "promptingStrategy", "includeTagsInCurrentFile" }`,
` -p, --parallelism Number of parallel workers (default: 20)`,
` --verbose Print detailed progress and error information`,
Expand Down
2 changes: 1 addition & 1 deletion extensions/copilot/test/pipeline/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export async function runInputPipeline(opts: RunPipelineOptions, log = console.l
await configService.setConfig(ConfigKey.TeamInternal.InlineEditsExtraDebounceEndOfLine, 0);
await configService.setConfig(ConfigKey.TeamInternal.InlineEditsExtraDebounceInlineSuggestion, 0);

const modelConfig = configService.getConfig(ConfigKey.TeamInternal.InlineEditsXtabProviderModelConfiguration);
const modelConfig = configService.getConfig(ConfigKey.Advanced.InlineEditsXtabProviderModelConfiguration);
const responseFormat = ResponseFormat.fromPromptingStrategy(modelConfig?.promptingStrategy);

log(` Local model configuration: ${JSON.stringify(modelConfig)}`);
Expand Down
2 changes: 1 addition & 1 deletion extensions/copilot/test/pipeline/test/fixtures/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"github.copilot.chat.advanced.inlineEdits.xtabProvider.modelConfiguration": {
"github.copilot.chat.inlineEdits.xtabProvider.modelConfiguration": {
"modelName": "test-model",
"promptingStrategy": "patchBased02",
"includeTagsInCurrentFile": true,
Expand Down
Loading