Microsoft Foundry: tighten onboarding retry and model selection hooks
Only retry Azure login with an explicit tenant when the CLI failure actually points to tenant or subscription scope, keep HTTP 400 connection checks informative without treating them as a silent success, and move the model-selection hook onto the provider so manual Foundry setups can preserve GPT-5 family hints and resolve the right runtime endpoint.
This commit is contained in:
parent
0c1efec1c9
commit
41cc7c8bf1
@ -529,7 +529,18 @@ async function promptEndpointAndModelManually(ctx: ProviderAuthContext): Promise
|
||||
},
|
||||
}),
|
||||
).trim();
|
||||
return { endpoint, modelId, modelNameHint: modelId };
|
||||
const modelNameHintInput = String(
|
||||
await ctx.prompter.text({
|
||||
message: "Underlying Azure model family (optional)",
|
||||
initialValue: modelId,
|
||||
placeholder: "gpt-5.4, gpt-4o, etc.",
|
||||
}),
|
||||
).trim();
|
||||
return {
|
||||
endpoint,
|
||||
modelId,
|
||||
modelNameHint: modelNameHintInput || modelId,
|
||||
};
|
||||
}
|
||||
|
||||
async function promptApiKeyEndpointAndModel(ctx: ProviderAuthContext): Promise<FoundrySelection> {
|
||||
@ -711,6 +722,13 @@ async function loginWithTenantFallback(ctx: ProviderAuthContext): Promise<{
|
||||
return { account: getLoggedInAccount() };
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
const isAzureTenantError =
|
||||
/AADSTS/i.test(message) ||
|
||||
/no subscriptions found/i.test(message) ||
|
||||
/tenant/i.test(message);
|
||||
if (!isAzureTenantError) {
|
||||
throw error;
|
||||
}
|
||||
const tenantId = await promptTenantId(ctx, {
|
||||
suggestions: extractTenantSuggestions(message),
|
||||
required: true,
|
||||
@ -872,7 +890,15 @@ const entraIdAuthMethod = {
|
||||
"Connection Test",
|
||||
);
|
||||
} else {
|
||||
await ctx.prompter.note("Connection test successful!", "✓");
|
||||
const statusNote = res.status === 400 ? " (400 Bad Request — endpoint reachable)" : "";
|
||||
const statusBody = res.status === 400 ? await res.text().catch(() => "") : "";
|
||||
await ctx.prompter.note(`Connection test successful!${statusNote}`, "✓");
|
||||
if (statusBody) {
|
||||
await ctx.prompter.note(
|
||||
`Endpoint response: ${statusBody.slice(0, 200)}`,
|
||||
"Connection Test",
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
await ctx.prompter.note(
|
||||
@ -904,41 +930,6 @@ const entraIdAuthMethod = {
|
||||
],
|
||||
});
|
||||
},
|
||||
onModelSelected: async (ctx: ProviderModelSelectedContext) => {
|
||||
const providerConfig = ctx.config.models?.providers?.[PROVIDER_ID];
|
||||
if (!providerConfig || !ctx.model.startsWith(`${PROVIDER_ID}/`)) {
|
||||
return;
|
||||
}
|
||||
const selectedModelId = ctx.model.slice(`${PROVIDER_ID}/`.length);
|
||||
const existingModel = providerConfig.models.find((model: { id: string }) => model.id === selectedModelId);
|
||||
const selectedModelNameHint = resolveConfiguredModelNameHint(
|
||||
selectedModelId,
|
||||
existingModel?.name,
|
||||
);
|
||||
const selectedModelCompat = buildFoundryModelCompat(selectedModelId, selectedModelNameHint);
|
||||
const providerEndpoint = normalizeFoundryEndpoint(providerConfig.baseUrl ?? "");
|
||||
const nextProviderConfig: ModelProviderConfig = {
|
||||
...providerConfig,
|
||||
baseUrl: buildFoundryProviderBaseUrl(providerEndpoint, selectedModelId, selectedModelNameHint),
|
||||
api: resolveFoundryApi(selectedModelId, selectedModelNameHint),
|
||||
models: [
|
||||
{
|
||||
...(existingModel ?? {
|
||||
id: selectedModelId,
|
||||
name: selectedModelId,
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 128_000,
|
||||
maxTokens: 16_384,
|
||||
}),
|
||||
...(selectedModelCompat ? { compat: selectedModelCompat } : {}),
|
||||
},
|
||||
],
|
||||
};
|
||||
applyFoundryProfileBinding(ctx.config, `${PROVIDER_ID}:entra`);
|
||||
applyFoundryProviderConfig(ctx.config, nextProviderConfig);
|
||||
},
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -1017,8 +1008,9 @@ function refreshEntraToken(params?: {
|
||||
tenantId?: string;
|
||||
}): { apiKey: string; expiresAt: number } {
|
||||
const result = getAccessTokenResult(params);
|
||||
const expiresAt = result.expiresOn
|
||||
? new Date(result.expiresOn).getTime()
|
||||
const rawExpiry = result.expiresOn ? new Date(result.expiresOn).getTime() : Number.NaN;
|
||||
const expiresAt = Number.isFinite(rawExpiry)
|
||||
? rawExpiry
|
||||
: Date.now() + 55 * 60 * 1000; // default ~55 min
|
||||
cachedTokens.set(getFoundryTokenCacheKey(params), {
|
||||
token: result.accessToken,
|
||||
@ -1045,6 +1037,43 @@ export default definePluginEntry({
|
||||
capabilities: {
|
||||
providerFamily: "openai",
|
||||
},
|
||||
onModelSelected: async (ctx: ProviderModelSelectedContext) => {
|
||||
const providerConfig = ctx.config.models?.providers?.[PROVIDER_ID];
|
||||
if (!providerConfig || !ctx.model.startsWith(`${PROVIDER_ID}/`)) {
|
||||
return;
|
||||
}
|
||||
const selectedModelId = ctx.model.slice(`${PROVIDER_ID}/`.length);
|
||||
const existingModel = providerConfig.models.find(
|
||||
(model: { id: string }) => model.id === selectedModelId,
|
||||
);
|
||||
const selectedModelNameHint = resolveConfiguredModelNameHint(
|
||||
selectedModelId,
|
||||
existingModel?.name,
|
||||
);
|
||||
const selectedModelCompat = buildFoundryModelCompat(selectedModelId, selectedModelNameHint);
|
||||
const providerEndpoint = normalizeFoundryEndpoint(providerConfig.baseUrl ?? "");
|
||||
const nextProviderConfig: ModelProviderConfig = {
|
||||
...providerConfig,
|
||||
baseUrl: buildFoundryProviderBaseUrl(providerEndpoint, selectedModelId, selectedModelNameHint),
|
||||
api: resolveFoundryApi(selectedModelId, selectedModelNameHint),
|
||||
models: [
|
||||
{
|
||||
...(existingModel ?? {
|
||||
id: selectedModelId,
|
||||
name: selectedModelId,
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 128_000,
|
||||
maxTokens: 16_384,
|
||||
}),
|
||||
...(selectedModelCompat ? { compat: selectedModelCompat } : {}),
|
||||
},
|
||||
],
|
||||
};
|
||||
applyFoundryProfileBinding(ctx.config, `${PROVIDER_ID}:entra`);
|
||||
applyFoundryProviderConfig(ctx.config, nextProviderConfig);
|
||||
},
|
||||
normalizeResolvedModel: ({ modelId, model }) => {
|
||||
const endpoint = extractFoundryEndpoint(model.baseUrl ?? "");
|
||||
if (!endpoint) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user