Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/byok/vscode-node/azureProvider.ts
13399 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as vscode from 'vscode';
7
import { CancellationToken, LanguageModelChatMessage, LanguageModelChatMessage2, LanguageModelResponsePart2, Progress, ProvideLanguageModelChatResponseOptions } from 'vscode';
8
import { AzureAuthMode, ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';
9
import { isEndpointEditToolName } from '../../../platform/endpoint/common/endpointProvider';
10
import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';
11
import { ILogService } from '../../../platform/log/common/logService';
12
import { IFetcherService } from '../../../platform/networking/common/fetcherService';
13
import { IExperimentationService } from '../../../platform/telemetry/common/nullExperimentationService';
14
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
15
import { resolveModelInfo } from '../common/byokProvider';
16
import { AzureOpenAIEndpoint } from '../node/azureOpenAIEndpoint';
17
import { OpenAICompatibleLanguageModelChatInformation } from './abstractLanguageModelChatProvider';
18
import { IBYOKStorageService } from './byokStorageService';
19
import { AbstractCustomOAIBYOKModelProvider, CustomOAIModelProviderConfig, hasExplicitApiPath } from './customOAIProvider';
20
21
export function resolveAzureUrl(modelId: string, url: string): string {
22
// The fully resolved url was already passed in
23
if (hasExplicitApiPath(url)) {
24
return url;
25
}
26
27
// Remove the trailing slash
28
if (url.endsWith('/')) {
29
url = url.slice(0, -1);
30
}
31
// if url ends with `/v1` remove it
32
if (url.endsWith('/v1')) {
33
url = url.slice(0, -3);
34
}
35
36
// Default to chat completions for base URLs
37
const defaultApiPath = '/chat/completions';
38
39
if (url.includes('models.ai.azure.com') || url.includes('inference.ml.azure.com')) {
40
return `${url}/v1${defaultApiPath}`;
41
} else if (url.includes('openai.azure.com')) {
42
return `${url}/openai/deployments/${modelId}${defaultApiPath}?api-version=2025-01-01-preview`;
43
} else {
44
throw new Error(`Unrecognized Azure deployment URL: ${url}`);
45
}
46
}
47
48
export class AzureBYOKModelProvider extends AbstractCustomOAIBYOKModelProvider {
49
50
static readonly providerName = 'Azure';
51
52
constructor(
53
byokStorageService: IBYOKStorageService,
54
@IConfigurationService configurationService: IConfigurationService,
55
@ILogService logService: ILogService,
56
@IFetcherService fetcherService: IFetcherService,
57
@IInstantiationService instantiationService: IInstantiationService,
58
@IExperimentationService expService: IExperimentationService,
59
@IVSCodeExtensionContext extensionContext: IVSCodeExtensionContext
60
) {
61
super(
62
AzureBYOKModelProvider.providerName.toLowerCase(),
63
AzureBYOKModelProvider.providerName,
64
byokStorageService,
65
logService,
66
fetcherService,
67
instantiationService,
68
configurationService,
69
expService,
70
extensionContext
71
);
72
this.migrateExistingConfigs();
73
}
74
75
// TODO: Remove this after 6 months
76
private async migrateExistingConfigs(): Promise<void> {
77
await this.migrateConfig(ConfigKey.Deprecated.AzureModels, AzureBYOKModelProvider.providerName, AzureBYOKModelProvider.providerName);
78
await this._configurationService.setConfig(ConfigKey.Deprecated.AzureAuthType, undefined);
79
}
80
81
protected override resolveUrl(modelId: string, url: string): string {
82
return resolveAzureUrl(modelId, url);
83
}
84
85
override async provideLanguageModelChatResponse(
86
model: OpenAICompatibleLanguageModelChatInformation<CustomOAIModelProviderConfig>,
87
messages: Array<LanguageModelChatMessage | LanguageModelChatMessage2>,
88
options: ProvideLanguageModelChatResponseOptions,
89
progress: Progress<LanguageModelResponsePart2>,
90
token: CancellationToken
91
): Promise<void> {
92
if (model.configuration?.apiKey) {
93
return super.provideLanguageModelChatResponse(model, messages, options, progress, token);
94
}
95
96
const session: vscode.AuthenticationSession = await vscode.authentication.getSession(
97
AzureAuthMode.MICROSOFT_AUTH_PROVIDER,
98
[AzureAuthMode.COGNITIVE_SERVICES_SCOPE],
99
{
100
createIfNone: true,
101
silent: false
102
}
103
);
104
105
const url = this.resolveUrl(model.id, model.url);
106
const modelConfiguration = model.configuration?.models?.find(m => m.id === model.id);
107
const modelCapabilities = {
108
maxInputTokens: model.maxInputTokens,
109
maxOutputTokens: model.maxOutputTokens,
110
toolCalling: !!model.capabilities?.toolCalling || false,
111
vision: !!model.capabilities?.imageInput || false,
112
name: model.name,
113
url,
114
thinking: modelConfiguration?.thinking,
115
streaming: modelConfiguration?.streaming,
116
requestHeaders: modelConfiguration?.requestHeaders,
117
editTools: model.capabilities?.editTools?.filter(isEndpointEditToolName),
118
zeroDataRetentionEnabled: modelConfiguration?.zeroDataRetentionEnabled
119
};
120
const modelInfo = resolveModelInfo(model.id, this._name, undefined, modelCapabilities);
121
122
const openAIChatEndpoint = this._instantiationService.createInstance(
123
AzureOpenAIEndpoint,
124
modelInfo,
125
session.accessToken, // Pass Entra ID token
126
url
127
);
128
129
return this._lmWrapper.provideLanguageModelResponse(
130
openAIChatEndpoint,
131
messages,
132
options,
133
options.requestInitiator,
134
progress,
135
token
136
);
137
}
138
}
139
140