From 1fc85054b43986d0c5a9ab596eebda4096e3e33f Mon Sep 17 00:00:00 2001 From: redshift <213178690+1ftredsh@users.noreply.github.com> Date: Thu, 24 Sep 2026 20:46:47 +0530 Subject: [PATCH] fix: resolve mapped model ids in model list and per-model views The SDK now ranks/aggregates providers serving canonical models under mapped variant ids or aliases (e.g. z-ai-glm-5-3, claude-opus-5-5), but the daemon still matched exact ids in two places, causing 'Model not found' when the cheapest provider only knew a variant id: - getModelProviders metadata lookup: use findModelForId; prefer metadata from a provider serving the canonical id natively, fall back to the cheapest mapped provider. Return the requested canonical id. - getCachedModels aggregation: group best-priced models by canonicalIdForModel so variants fold into one entry. Verified live: claude-opus-5.5 now merges 13 providers (cypherpunk #1 enabled at ~2562 sats/M), glm-5.3 shows cypherpunk via z-ai-glm-5-3. --- src/daemon/models.ts | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/daemon/models.ts b/src/daemon/models.ts index bdc4152..4fb8c66 100644 --- a/src/daemon/models.ts +++ b/src/daemon/models.ts @@ -1,4 +1,11 @@ -import { ModelManager, ProviderManager, type SdkStore } from "@routstr/sdk"; +import { + canonicalIdForModel, + findModelForId, + ModelManager, + ProviderManager, + type Model, + type SdkStore, +} from "@routstr/sdk"; import type { ExposedModel } from "./types"; import { logger } from "../utils/logger"; @@ -66,13 +73,16 @@ export function createModelService( for (const model of models as PricedModel[]) { if (!model.sats_pricing) continue; - const existing = bestById.get(model.id); + // Group by canonical id so providers serving the same model under a + // mapped variant id or alias fold into one aggregate entry. + const canonicalId = canonicalIdForModel(model as Model); + const existing = bestById.get(canonicalId); if ( !existing || (model.sats_pricing.completion ?? 0) < (existing.sats_pricing?.completion ?? 0) ) { - bestById.set(model.id, model); + bestById.set(canonicalId, model); } } } @@ -201,18 +211,35 @@ export function createModelService( return null; } - // Get model metadata from first (cheapest) provider + // Get model metadata. Prefer a provider that serves the requested + // canonical id natively (nicest name/description); fall back to the + // cheapest provider matched through the SDK's model mappings, since it + // may only know the model under a mapped variant id or alias. const cheapest = providers[0]!; const allModels = modelManager.getAllCachedModels(); - const firstProvider = allModels[cheapest.baseUrl]; - const modelInfo = firstProvider?.find((m: { id: string }) => m.id === modelId); + let modelInfo: Model | undefined; + for (const p of providers) { + const exact = (allModels[p.baseUrl] as Model[] | undefined)?.find( + (m) => m.id === modelId, + ); + if (exact) { + modelInfo = exact; + break; + } + } + if (!modelInfo) { + const firstProvider = allModels[cheapest.baseUrl]; + modelInfo = firstProvider + ? findModelForId(firstProvider as Model[], modelId) + : undefined; + } if (!modelInfo) { return null; } return { - id: modelInfo.id, + id: modelId, name: modelInfo.name, description: modelInfo.description, context_length: modelInfo.context_length,