diff --git a/src/app/(dashboard)/dashboard/models/page.js b/src/app/(dashboard)/dashboard/models/page.js index 4fd87b7f..0a420134 100644 --- a/src/app/(dashboard)/dashboard/models/page.js +++ b/src/app/(dashboard)/dashboard/models/page.js @@ -21,8 +21,14 @@ function groupModelsByProvider(models) { groups[key] = { provider: model.provider, models: [], + modelIds: new Set(), }; } + + // The API normally supplies unique models. Ignore a duplicate defensively + // so a malformed response cannot produce duplicate React keys. + if (groups[key].modelIds.has(model.fullModel)) return groups; + groups[key].modelIds.add(model.fullModel); groups[key].models.push(model); return groups; }, {}); diff --git a/src/shared/constants/models.js b/src/shared/constants/models.js index 297daae7..8f99a2b6 100644 --- a/src/shared/constants/models.js +++ b/src/shared/constants/models.js @@ -32,9 +32,20 @@ export function isValidModel(aliasOrId, modelId) { return models.some(m => m.id === modelId); } -// Legacy AI_MODELS for backward compatibility +// Legacy AI_MODELS for backward compatibility. A model can be declared under +// multiple service kinds (for example, Gemini chat and speech-to-text), but +// this flat catalog has no kind field. Keep its provider/model identity unique. +const seenModelIds = new Set(); + export const AI_MODELS = Object.entries(MODELS).flatMap(([alias, models]) => - models.map(m => ({ provider: alias, model: m.id, name: m.name })) + models + .filter((model) => { + const modelKey = `${alias}/${model.id}`; + if (seenModelIds.has(modelKey)) return false; + seenModelIds.add(modelKey); + return true; + }) + .map((model) => ({ provider: alias, model: model.id, name: model.name })) ); export const getModelKind = (m, fallback = null) => m?.kind || m?.type || fallback;