Path: blob/main/extensions/copilot/src/extension/byok/vscode-node/azureProvider.ts
13399 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as vscode from 'vscode';6import { CancellationToken, LanguageModelChatMessage, LanguageModelChatMessage2, LanguageModelResponsePart2, Progress, ProvideLanguageModelChatResponseOptions } from 'vscode';7import { AzureAuthMode, ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';8import { isEndpointEditToolName } from '../../../platform/endpoint/common/endpointProvider';9import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';10import { ILogService } from '../../../platform/log/common/logService';11import { IFetcherService } from '../../../platform/networking/common/fetcherService';12import { IExperimentationService } from '../../../platform/telemetry/common/nullExperimentationService';13import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';14import { resolveModelInfo } from '../common/byokProvider';15import { AzureOpenAIEndpoint } from '../node/azureOpenAIEndpoint';16import { OpenAICompatibleLanguageModelChatInformation } from './abstractLanguageModelChatProvider';17import { IBYOKStorageService } from './byokStorageService';18import { AbstractCustomOAIBYOKModelProvider, CustomOAIModelProviderConfig, hasExplicitApiPath } from './customOAIProvider';1920export function resolveAzureUrl(modelId: string, url: string): string {21// The fully resolved url was already passed in22if (hasExplicitApiPath(url)) {23return url;24}2526// Remove the trailing slash27if (url.endsWith('/')) {28url = url.slice(0, -1);29}30// if url ends with `/v1` remove it31if (url.endsWith('/v1')) {32url = url.slice(0, -3);33}3435// Default to chat completions for base URLs36const defaultApiPath = '/chat/completions';3738if (url.includes('models.ai.azure.com') || url.includes('inference.ml.azure.com')) {39return `${url}/v1${defaultApiPath}`;40} else if (url.includes('openai.azure.com')) {41return `${url}/openai/deployments/${modelId}${defaultApiPath}?api-version=2025-01-01-preview`;42} else {43throw new Error(`Unrecognized Azure deployment URL: ${url}`);44}45}4647export class AzureBYOKModelProvider extends AbstractCustomOAIBYOKModelProvider {4849static readonly providerName = 'Azure';5051constructor(52byokStorageService: IBYOKStorageService,53@IConfigurationService configurationService: IConfigurationService,54@ILogService logService: ILogService,55@IFetcherService fetcherService: IFetcherService,56@IInstantiationService instantiationService: IInstantiationService,57@IExperimentationService expService: IExperimentationService,58@IVSCodeExtensionContext extensionContext: IVSCodeExtensionContext59) {60super(61AzureBYOKModelProvider.providerName.toLowerCase(),62AzureBYOKModelProvider.providerName,63byokStorageService,64logService,65fetcherService,66instantiationService,67configurationService,68expService,69extensionContext70);71this.migrateExistingConfigs();72}7374// TODO: Remove this after 6 months75private async migrateExistingConfigs(): Promise<void> {76await this.migrateConfig(ConfigKey.Deprecated.AzureModels, AzureBYOKModelProvider.providerName, AzureBYOKModelProvider.providerName);77await this._configurationService.setConfig(ConfigKey.Deprecated.AzureAuthType, undefined);78}7980protected override resolveUrl(modelId: string, url: string): string {81return resolveAzureUrl(modelId, url);82}8384override async provideLanguageModelChatResponse(85model: OpenAICompatibleLanguageModelChatInformation<CustomOAIModelProviderConfig>,86messages: Array<LanguageModelChatMessage | LanguageModelChatMessage2>,87options: ProvideLanguageModelChatResponseOptions,88progress: Progress<LanguageModelResponsePart2>,89token: CancellationToken90): Promise<void> {91if (model.configuration?.apiKey) {92return super.provideLanguageModelChatResponse(model, messages, options, progress, token);93}9495const session: vscode.AuthenticationSession = await vscode.authentication.getSession(96AzureAuthMode.MICROSOFT_AUTH_PROVIDER,97[AzureAuthMode.COGNITIVE_SERVICES_SCOPE],98{99createIfNone: true,100silent: false101}102);103104const url = this.resolveUrl(model.id, model.url);105const modelConfiguration = model.configuration?.models?.find(m => m.id === model.id);106const modelCapabilities = {107maxInputTokens: model.maxInputTokens,108maxOutputTokens: model.maxOutputTokens,109toolCalling: !!model.capabilities?.toolCalling || false,110vision: !!model.capabilities?.imageInput || false,111name: model.name,112url,113thinking: modelConfiguration?.thinking,114streaming: modelConfiguration?.streaming,115requestHeaders: modelConfiguration?.requestHeaders,116editTools: model.capabilities?.editTools?.filter(isEndpointEditToolName),117zeroDataRetentionEnabled: modelConfiguration?.zeroDataRetentionEnabled118};119const modelInfo = resolveModelInfo(model.id, this._name, undefined, modelCapabilities);120121const openAIChatEndpoint = this._instantiationService.createInstance(122AzureOpenAIEndpoint,123modelInfo,124session.accessToken, // Pass Entra ID token125url126);127128return this._lmWrapper.provideLanguageModelResponse(129openAIChatEndpoint,130messages,131options,132options.requestInitiator,133progress,134token135);136}137}138139140