Hide health-check excluded models from /v1/models catalog (#10026)

Mirror the request-time exclusion rule (provider_specific_data.excludedModels)
in the unified catalog builder: a model is hidden when its provider has
connections but none of them is eligible for it. Applied across the
PROVIDER_MODELS, synced, custom, alias-backed, and managed-fallback loops
so ghost models no longer appear as available.

Co-authored-by: ritheshcn25 <ritheshcn25@users.noreply.github.com>
This commit is contained in:
ritheshcn25
2026-08-13 00:43:38 -03:00
committed by GitHub
parent 918fba5e39
commit 5f0a394091

View File

@@ -332,6 +332,18 @@ async function buildUnifiedModelsResponseCore(
return collected;
};
// Health-check exclusions (provider_specific_data.excludedModels) are enforced
// at request time in getProviderCredentials(); mirror the same rule in the
// catalog so ghost models do not appear as available. A model is hidden when
// the provider HAS connections but NONE of them is eligible for it.
const isExcludedByProviderConnections = (providerKey: string, modelId: string) => {
const providerId = aliasToProviderId[providerKey] || providerKey;
const alias = providerIdToAlias[providerId] || providerKey;
const providerConnections = getConnectionsForProvider(providerId, alias, providerKey);
if (providerConnections.length === 0) return false; // noAuth / no DB row: keep
return !hasEligibleConnectionForModel(providerConnections, modelId);
};
const providerSupportsModel = (providerKey: string, modelId: string) => {
const providerId = aliasToProviderId[providerKey] || providerKey;
const alias = providerIdToAlias[providerId] || providerKey;
@@ -703,6 +715,7 @@ async function buildUnifiedModelsResponseCore(
if (!providerSupportsModel(canonicalProviderId, model.id)) continue;
const aliasId = `${alias}/${model.id}`;
if (getModelIsHidden(canonicalProviderId, model.id)) continue;
if (isExcludedByProviderConnections(canonicalProviderId, model.id)) continue;
if (shouldHidePaid(canonicalProviderId, model.id, (model as { pricing?: unknown }).pricing))
continue;
@@ -797,6 +810,7 @@ async function buildUnifiedModelsResponseCore(
continue;
}
if (getModelIsHidden(providerId, sm.id)) continue;
if (isExcludedByProviderConnections(canonicalProviderId, sm.id)) continue;
// #6457: some upstream discovery catalogs (e.g. HuggingFace's live
// `/v1/models`) return image/diffusion models with no modality info,
// so `endpoints` below would default to ["chat"] and misrepresent
@@ -1173,6 +1187,7 @@ async function buildUnifiedModelsResponseCore(
if (!modelId) continue;
if (model.isHidden === true) continue;
if (getModelIsHidden(canonicalProviderId, modelId)) continue;
if (isExcludedByProviderConnections(canonicalProviderId, modelId)) continue;
// #6328: apply hidePaidModels to user-defined custom rows too.
// Custom entries do not carry pricing, so shouldHidePaid() decides
// via FREE_MODEL_IDS_BY_PROVIDER — matches synced/PROVIDER_MODELS.
@@ -1305,6 +1320,7 @@ async function buildUnifiedModelsResponseCore(
}
if (getModelIsHidden(canonicalProviderId, modelId)) continue;
if (isExcludedByProviderConnections(canonicalProviderId, modelId)) continue;
// #6328: apply hidePaidModels to alias-backed rows too. Alias mappings
// point at providerKey/modelId with no pricing, so shouldHidePaid()
// decides via the FREE_MODEL_IDS_BY_PROVIDER catalog tier.
@@ -1377,6 +1393,7 @@ async function buildUnifiedModelsResponseCore(
const modelId = typeof model.id === "string" ? model.id : null;
if (!modelId) continue;
if (getModelIsHidden(canonicalProviderId, modelId)) continue;
if (isExcludedByProviderConnections(canonicalProviderId, modelId)) continue;
// #6328: apply hidePaidModels to managed-fallback rows too. Compatible
// provider fallbacks lack pricing; shouldHidePaid() decides via the
// FREE_MODEL_IDS_BY_PROVIDER catalog tier.