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
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export function byokKnownModelToAPIInfo(providerName: string, id: string, capabi
family: id,
tooltip: `${capabilities.name} is contributed via the ${providerName} provider.`,
multiplierNumeric: 0,
isUserSelectable: true,
capabilities: {
toolCalling: capabilities.toolCalling,
imageInput: capabilities.vision
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ICAPIClientService } from '../../../platform/endpoint/common/capiClient
import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';
import { ILogService } from '../../../platform/log/common/logService';
import { IFetcherService } from '../../../platform/networking/common/fetcherService';
import { Disposable } from '../../../util/vs/base/common/lifecycle';
import { Disposable, DisposableStore } from '../../../util/vs/base/common/lifecycle';
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
import { BYOKKnownModels, isBYOKEnabled } from '../../byok/common/byokProvider';
import { IExtensionContribution } from '../../common/contributions';
Expand All @@ -26,6 +26,7 @@ export class BYOKContrib extends Disposable implements IExtensionContribution {
public readonly id: string = 'byok-contribution';
private readonly _byokStorageService: IBYOKStorageService;
private readonly _providers: Map<string, LanguageModelChatProvider<LanguageModelChatInformation>> = new Map();
private readonly _byokRegistrations = this._register(new DisposableStore());
private _byokProvidersRegistered = false;

constructor(
Expand All @@ -46,7 +47,17 @@ export class BYOKContrib extends Disposable implements IExtensionContribution {
}

private async _authChange(authService: IAuthenticationService, instantiationService: IInstantiationService) {
if (authService.copilotToken && isBYOKEnabled(authService.copilotToken, this._capiClientService) && !this._byokProvidersRegistered) {
const byokEnabled = authService.copilotToken && isBYOKEnabled(authService.copilotToken, this._capiClientService);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

byokEnabled is derived via authService.copilotToken && ..., which yields boolean | undefined (and relies on truthiness checks). Consider coercing to a strict boolean (e.g. !!authService.copilotToken && ...) so the intent is explicit and the variable’s type stays boolean.

Suggested change
const byokEnabled = authService.copilotToken && isBYOKEnabled(authService.copilotToken, this._capiClientService);
const byokEnabled = !!authService.copilotToken && isBYOKEnabled(authService.copilotToken, this._capiClientService);

Copilot uses AI. Check for mistakes.

if (!byokEnabled && this._byokProvidersRegistered) {
this._logService.info('BYOK: Disabling BYOK providers due to account change.');
this._byokRegistrations.clear();
this._providers.clear();
this._byokProvidersRegistered = false;
return;
}

if (byokEnabled && !this._byokProvidersRegistered) {
this._byokProvidersRegistered = true;
// Update known models list from CDN so all providers have the same list
const knownModels = await this.fetchKnownModelList(this._fetcherService);
Expand All @@ -63,7 +74,7 @@ export class BYOKContrib extends Disposable implements IExtensionContribution {
this._providers.set(CustomOAIBYOKModelProvider.providerName.toLowerCase(), instantiationService.createInstance(CustomOAIBYOKModelProvider, this._byokStorageService));

for (const [providerName, provider] of this._providers) {
this._store.add(lm.registerLanguageModelChatProvider(providerName, provider));
this._byokRegistrations.add(lm.registerLanguageModelChatProvider(providerName, provider));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export class ContextKeysContribution extends Disposable {

void this._inspectContext().catch(console.error);
void this._updatePermissiveSessionContext().catch(console.error);
void this._updateClientByokEnabledContext().catch(console.error);
this._register(_authenticationService.onDidAuthenticationChange(async () => await this._onAuthenticationChange()));
this._register(commands.registerCommand('github.copilot.refreshToken', async () => await this._inspectContext()));
this._register(commands.registerCommand('github.copilot.debug.showChatLogView', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ class ActionsColumnRenderer extends ModelsTableColumnRenderer<IActionsColumnTemp
return;
}
await this.languageModelsService.removeLanguageModelsProviderGroup(vendorEntry.vendor.vendor, vendorEntry.group.name);
this.viewModel.refresh();
}
}));
} else if (vendorEntry.vendor.managementCommand) {
Expand Down Expand Up @@ -1336,7 +1337,8 @@ export class ChatModelsWidget extends Disposable {
}

private async addModelsForVendor(vendor: ILanguageModelProviderDescriptor): Promise<void> {
this.languageModelsService.configureLanguageModelsProviderGroup(vendor.vendor);
await this.languageModelsService.configureLanguageModelsProviderGroup(vendor.vendor);
await this.viewModel.refresh();
}

public layout(height: number, width: number): void {
Expand Down
25 changes: 25 additions & 0 deletions src/vs/workbench/contrib/chat/common/languageModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,7 @@ export class LanguageModelsService implements ILanguageModelsService {
}
}

const oldGroups = this._modelsGroups.get(vendorId) ?? [];
this._modelsGroups.set(vendorId, languageModelsGroups);
const oldModels = this._clearModelCache(vendorId);
let hasChanges = false;
Expand All @@ -941,6 +942,12 @@ export class LanguageModelsService implements ILanguageModelsService {
this._logService.trace(`[LM] Resolved language models for vendor ${vendorId}`, allModels);
hasChanges = hasChanges || oldModels.size > 0;

// Also detect group structure changes (added/removed groups, status changes)
// so the UI updates even when individual models haven't changed
if (!hasChanges) {
hasChanges = this._hasGroupStructureChanged(oldGroups, languageModelsGroups);
}

// Update per-model configurations for this vendor
this._clearModelConfigurations(vendorId);
for (const [identifier, config] of perModelConfigurations) {
Expand All @@ -957,6 +964,24 @@ export class LanguageModelsService implements ILanguageModelsService {
});
}

private _hasGroupStructureChanged(oldGroups: readonly ILanguageModelsGroup[], newGroups: readonly ILanguageModelsGroup[]): boolean {
if (oldGroups.length !== newGroups.length) {
return true;
}
for (let i = 0; i < oldGroups.length; i++) {
const oldGroup = oldGroups[i];
const newGroup = newGroups[i];
if (oldGroup.group?.name !== newGroup.group?.name
|| oldGroup.group?.vendor !== newGroup.group?.vendor
|| oldGroup.status?.message !== newGroup.status?.message
|| oldGroup.status?.severity !== newGroup.status?.severity
|| oldGroup.modelIdentifiers.length !== newGroup.modelIdentifiers.length) {
return true;
}
}
return false;
}

getLanguageModelGroups(vendor: string): ILanguageModelsGroup[] {
return this._modelsGroups.get(vendor) ?? [];
}
Expand Down
Loading