Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/proxyModels/node/proxyModelsService.ts
13400 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 { isDeepStrictEqual } from 'util';
7
import { ErrorUtils } from '../../../util/common/errors';
8
import { CancellationToken, CancellationTokenSource } from '../../../util/vs/base/common/cancellation';
9
import { Emitter } from '../../../util/vs/base/common/event';
10
import { Disposable } from '../../../util/vs/base/common/lifecycle';
11
import { autorun, observableFromEvent } from '../../../util/vs/base/common/observable';
12
import { CopilotToken } from '../../authentication/common/copilotToken';
13
import { ICopilotTokenStore } from '../../authentication/common/copilotTokenStore';
14
import { ICAPIClientService } from '../../endpoint/common/capiClient';
15
import { WireTypes } from '../../inlineEdits/common/dataTypes/inlineEditsModelsTypes';
16
import { ILogService } from '../../log/common/logService';
17
import { IFetcherService, Response } from '../../networking/common/fetcherService';
18
import { IProxyModelsService } from '../common/proxyModelsService';
19
20
export class ProxyModelsService extends Disposable implements IProxyModelsService {
21
readonly _serviceBrand: undefined;
22
23
private readonly _onModelListUpdated = this._register(new Emitter<void>());
24
public readonly onModelListUpdated = this._onModelListUpdated.event;
25
26
private _models: WireTypes.ModelList.t | undefined;
27
28
constructor(
29
@ICopilotTokenStore private readonly _tokenStore: ICopilotTokenStore,
30
@ICAPIClientService private readonly _capiClient: ICAPIClientService,
31
@IFetcherService private readonly _fetchService: IFetcherService,
32
@ILogService private readonly _logService: ILogService,
33
) {
34
super();
35
36
const copilotTokenObs = observableFromEvent(this, this._tokenStore.onDidStoreUpdate, () => this._tokenStore.copilotToken);
37
38
this._register(autorun(reader => {
39
const copilotToken = copilotTokenObs.read(reader);
40
const cts = new CancellationTokenSource();
41
this._fetchLatestModels(copilotToken, cts.token).then(models => {
42
if (models === undefined) {
43
return;
44
}
45
if (cts.token.isCancellationRequested) {
46
return;
47
}
48
if (isDeepStrictEqual(this._models, models)) {
49
return;
50
}
51
this._models = models;
52
this._onModelListUpdated.fire();
53
}).catch((e: unknown) => {
54
const err = ErrorUtils.fromUnknown(e);
55
this._logService.error(err, 'Failed to fetch models in autorun');
56
});
57
reader.store.add({ dispose: () => cts.dispose(true) });
58
}));
59
}
60
61
get models(): WireTypes.ModelList.t | undefined {
62
return this._models;
63
}
64
65
get nesModels(): WireTypes.Model.t[] | undefined {
66
return this._models?.models.filter(model => model.serviceType === 'NESChat');
67
}
68
69
get instantApplyModels(): WireTypes.Model.t[] | undefined {
70
return this._models?.models.filter(model => model.serviceType === 'InstantApplyChat');
71
}
72
73
private async _fetchLatestModels(copilotToken: CopilotToken | undefined, token: CancellationToken): Promise<WireTypes.ModelList.t | undefined> {
74
if (!copilotToken) {
75
return undefined;
76
}
77
78
const url = `${this._capiClient.proxyBaseURL}/models`;
79
80
const abortController = this._fetchService.makeAbortController();
81
const disposable = token.onCancellationRequested(() => abortController.abort());
82
83
let r: Response;
84
try {
85
r = await this._fetchService.fetch(url, {
86
headers: {
87
'Authorization': `Bearer ${copilotToken.token}`,
88
},
89
method: 'GET',
90
timeout: 10_000,
91
callSite: 'proxy-models',
92
signal: abortController.signal,
93
});
94
} catch (e: unknown) {
95
const err = ErrorUtils.fromUnknown(e);
96
this._logService.error(err, 'Failed to fetch model list');
97
return;
98
} finally {
99
disposable.dispose();
100
}
101
102
if (!r.ok) {
103
this._logService.error(`Failed to fetch model list: ${r.status} ${r.statusText}`);
104
return;
105
}
106
107
try {
108
const jsonData: unknown = await r.json();
109
const validatedData = WireTypes.ModelList.validator.validate(jsonData);
110
if (validatedData.error) {
111
throw new Error(`Invalid /models response data: ${validatedData.error.message}`); // TODO@ulugbekna: add telemetry
112
}
113
return validatedData.content;
114
} catch (e: unknown) {
115
const err = ErrorUtils.fromUnknown(e);
116
this._logService.error(err, 'Failed to process /models response');
117
return;
118
}
119
}
120
121
}
122
123