Skip to content

Commit dfa3a05

Browse files
committed
fix(microsoft-foundry): use shared filter for picker and persisted catalog
Address PR review feedback: `selectFoundryDeployment` derived a local `supported` list to drive the picker but the persistence path in auth.ts still mapped the raw `resourceDeployments` into `discoveredDeployments`, so mixed GPT+Claude resources hid Claude in the picker yet still wrote it into the saved provider catalog as a `microsoft-foundry/<deployment>` model that routes through /openai/v1. - Add `partitionFoundryDeployments` in shared.ts as the single source of truth for filtering. - `selectFoundryDeployment` now returns `{ selected, supported }` so callers can persist the same supported list the picker showed. - `auth.ts` uses the supported list when building `discoveredDeployments`, keeping the picker and catalog in sync.
1 parent 4c0f392 commit dfa3a05

3 files changed

Lines changed: 38 additions & 15 deletions

File tree

extensions/microsoft-foundry/auth.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,13 @@ export const entraIdAuthMethod: ProviderAuthMethod = {
121121
if (useDiscoveredResource) {
122122
const selectedResource = await selectFoundryResource(ctx, selectedSub);
123123
const resourceDeployments = listResourceDeployments(selectedResource, selectedSub.id);
124-
const selectedDeployment = await selectFoundryDeployment(
125-
ctx,
126-
selectedResource,
127-
resourceDeployments,
128-
);
129-
discoveredDeployments = resourceDeployments.map((deployment) =>
124+
const { selected: selectedDeployment, supported: supportedDeployments } =
125+
await selectFoundryDeployment(ctx, selectedResource, resourceDeployments);
126+
// Persist only supported (non-Anthropic) deployments so the saved
127+
// provider catalog matches what the picker offered. Mixed
128+
// resources previously leaked Claude deployments into the catalog
129+
// even though they were filtered from the picker. See #60546.
130+
discoveredDeployments = supportedDeployments.map((deployment) =>
130131
Object.assign(
131132
{ name: deployment.name },
132133
deployment.modelName ? { modelName: deployment.modelName } : {},

extensions/microsoft-foundry/onboard.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
requiresFoundryMaxCompletionTokens,
2626
DEFAULT_API,
2727
DEFAULT_GPT5_API,
28-
isAnthropicFoundryDeployment,
28+
partitionFoundryDeployments,
2929
usesFoundryResponsesByDefault,
3030
} from "./shared.js";
3131

@@ -167,15 +167,15 @@ export async function selectFoundryDeployment(
167167
ctx: ProviderAuthContext,
168168
resource: FoundryResourceOption,
169169
deployments: AzDeploymentSummary[],
170-
): Promise<AzDeploymentSummary> {
170+
): Promise<{ selected: AzDeploymentSummary; supported: AzDeploymentSummary[] }> {
171171
// Filter out Anthropic (Claude) deployments — they require a different API
172172
// shape (/anthropic/v1/messages) that the built-in provider does not support
173173
// yet. Use a custom provider pointed at the Anthropic Foundry endpoint
174-
// instead. See #60546.
175-
const anthropicNames = deployments
176-
.filter((d) => isAnthropicFoundryDeployment(d.modelName))
177-
.map((d) => d.name);
178-
const supported = deployments.filter((d) => !isAnthropicFoundryDeployment(d.modelName));
174+
// instead. See #60546. The same `supported` list is reused by the caller
175+
// when persisting the deployment catalog so unsupported entries never leak
176+
// through into the saved provider config.
177+
const { supported, anthropic } = partitionFoundryDeployments(deployments);
178+
const anthropicNames = anthropic.map((d) => d.name);
179179
if (anthropicNames.length > 0) {
180180
await ctx.prompter.note(
181181
[
@@ -205,7 +205,7 @@ export async function selectFoundryDeployment(
205205
if (supported.length === 1) {
206206
const only = supported[0];
207207
await ctx.prompter.note(`Using deployment: ${only.name}`, "Model Deployment");
208-
return only;
208+
return { selected: only, supported };
209209
}
210210
const selectedDeploymentName = await ctx.prompter.select({
211211
message: "Select model deployment",
@@ -217,7 +217,9 @@ export async function selectFoundryDeployment(
217217
.join(" | "),
218218
})),
219219
});
220-
return supported.find((deployment) => deployment.name === selectedDeploymentName) ?? supported[0];
220+
const selected =
221+
supported.find((deployment) => deployment.name === selectedDeploymentName) ?? supported[0];
222+
return { selected, supported };
221223
}
222224

223225
async function promptFoundryApi(

extensions/microsoft-foundry/shared.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,26 @@ export function isAnthropicFoundryDeployment(modelName?: string | null): boolean
134134
return normalized.startsWith("claude");
135135
}
136136

137+
/**
138+
* Split a list of Foundry deployments into ones supported by the built-in
139+
* provider and Anthropic (Claude) deployments that require a custom provider.
140+
* Single source of truth so the picker and the persisted catalog stay in sync.
141+
*/
142+
export function partitionFoundryDeployments<T extends { modelName?: string }>(
143+
deployments: readonly T[],
144+
): { supported: T[]; anthropic: T[] } {
145+
const supported: T[] = [];
146+
const anthropic: T[] = [];
147+
for (const deployment of deployments) {
148+
if (isAnthropicFoundryDeployment(deployment.modelName)) {
149+
anthropic.push(deployment);
150+
} else {
151+
supported.push(deployment);
152+
}
153+
}
154+
return { supported, anthropic };
155+
}
156+
137157
export function usesFoundryResponsesByDefault(value?: string | null): boolean {
138158
const normalized = normalizeFoundryModelName(value);
139159
if (!normalized) {

0 commit comments

Comments
 (0)