diff --git a/src/vs/workbench/contrib/chat/browser/languageModelsConfigurationService.ts b/src/vs/workbench/contrib/chat/browser/languageModelsConfigurationService.ts index 3424791911d..efed13bcbb7 100644 --- a/src/vs/workbench/contrib/chat/browser/languageModelsConfigurationService.ts +++ b/src/vs/workbench/contrib/chat/browser/languageModelsConfigurationService.ts @@ -20,7 +20,8 @@ import { ITextModel } from '../../../../editor/common/model.js'; import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; import { getCodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { ILanguageModelsConfigurationService, ILanguageModelsProviderGroup } from '../common/languageModelsConfiguration.js'; +import { SnippetController2 } from '../../../../editor/contrib/snippet/browser/snippetController2.js'; +import { ConfigureLanguageModelsOptions, ILanguageModelsConfigurationService, ILanguageModelsProviderGroup } from '../common/languageModelsConfiguration.js'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; @@ -148,9 +149,9 @@ export class LanguageModelsConfigurationService extends Disposable implements IL await this.updateLanguageModelsConfiguration(); } - async configureLanguageModels(range?: IRange): Promise { + async configureLanguageModels(options?: ConfigureLanguageModelsOptions): Promise { const editor = await this.editorGroupsService.activeGroup.openEditor(this.textEditorService.createTextEditor({ resource: this.modelsConfigurationFile })); - if (!editor || !range) { + if (!editor || !options?.group) { return; } @@ -159,10 +160,29 @@ export class LanguageModelsConfigurationService extends Disposable implements IL return; } - const position = { lineNumber: range.startLineNumber, column: range.startColumn }; - codeEditor.setPosition(position); - codeEditor.revealPositionNearTop(position); - codeEditor.focus(); + if (!options.group.range) { + return; + } + + if (options.snippet) { + // Insert snippet at the end of the last property line (before the closing brace line), with comma prepended + const model = codeEditor.getModel(); + if (!model) { + return; + } + const lastPropertyLine = options.group.range.endLineNumber - 1; + const lastPropertyLineLength = model.getLineLength(lastPropertyLine); + const insertPosition = { lineNumber: lastPropertyLine, column: lastPropertyLineLength + 1 }; + codeEditor.setPosition(insertPosition); + codeEditor.revealPositionNearTop(insertPosition); + codeEditor.focus(); + SnippetController2.get(codeEditor)?.insert(',\n' + options.snippet); + } else { + const position = { lineNumber: options.group.range.startLineNumber, column: options.group.range.startColumn }; + codeEditor.setPosition(position); + codeEditor.revealPositionNearTop(position); + codeEditor.focus(); + } } private async withLanguageModelsProviderGroups(update?: (languageModelsProviderGroups: LanguageModelsProviderGroups) => Promise): Promise { diff --git a/src/vs/workbench/contrib/chat/common/languageModels.ts b/src/vs/workbench/contrib/chat/common/languageModels.ts index e8ea0fb5edd..f1f0ad87618 100644 --- a/src/vs/workbench/contrib/chat/common/languageModels.ts +++ b/src/vs/workbench/contrib/chat/common/languageModels.ts @@ -849,7 +849,8 @@ export class LanguageModelsService implements ILanguageModelsService { : await this._languageModelsConfigurationService.addLanguageModelsProviderGroup(languageModelProviderGroup); if (vendor.configuration && this.canConfigure(configuration ?? {}, vendor.configuration)) { - await this._languageModelsConfigurationService.configureLanguageModels(saved.range); + const snippet = this.getSnippetForFirstUnconfiguredProperty(configuration ?? {}, vendor.configuration); + await this._languageModelsConfigurationService.configureLanguageModels({ group: saved, snippet }); } } catch (error) { if (isCancellationError(error)) { @@ -901,6 +902,25 @@ export class LanguageModelsService implements ILanguageModelsService { return false; } + private getSnippetForFirstUnconfiguredProperty(configuration: IStringDictionary, schema: IJSONSchema): string | undefined { + if (!schema.properties) { + return undefined; + } + for (const property of Object.keys(schema.properties)) { + if (configuration[property] === undefined) { + const propertySchema = schema.properties[property]; + if (propertySchema && typeof propertySchema !== 'boolean' && propertySchema.defaultSnippets?.[0]) { + const snippet = propertySchema.defaultSnippets[0]; + let bodyText = snippet.bodyText ?? JSON.stringify(snippet.body, null, '\t'); + // Handle ^ prefix for raw values (numbers/booleans) - remove quotes around ^-prefixed values + bodyText = bodyText.replace(/"(\^[^"]*)"/g, (_, value) => value.substring(1)); + return `"${property}": ${bodyText}`; + } + } + } + return undefined; + } + private async promptForName(languageModelProviderGroups: readonly ILanguageModelsProviderGroup[], vendor: IUserFriendlyLanguageModel, existing: ILanguageModelsProviderGroup | undefined): Promise { let providerGroupName = existing?.name; if (!providerGroupName) { diff --git a/src/vs/workbench/contrib/chat/common/languageModelsConfiguration.ts b/src/vs/workbench/contrib/chat/common/languageModelsConfiguration.ts index 9573426343e..4f0dfa41691 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelsConfiguration.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelsConfiguration.ts @@ -11,6 +11,11 @@ import { IStringDictionary } from '../../../../base/common/collections.js'; export const ILanguageModelsConfigurationService = createDecorator('ILanguageModelsConfigurationService'); +export interface ConfigureLanguageModelsOptions { + group: ILanguageModelsProviderGroup; + snippet?: string; +} + export interface ILanguageModelsConfigurationService { readonly _serviceBrand: undefined; @@ -26,7 +31,7 @@ export interface ILanguageModelsConfigurationService { removeLanguageModelsProviderGroup(languageModelGroup: ILanguageModelsProviderGroup): Promise; - configureLanguageModels(range?: IRange): Promise; + configureLanguageModels(options?: ConfigureLanguageModelsOptions): Promise; } export interface ILanguageModelsProviderGroup extends IStringDictionary {