(raw: unknown)
| 43 | const DEFAULT_URL = "https://models.dev/api.json"; |
| 44 | |
| 45 | export function parseModelsDev(raw: unknown): ModelsDevApi { |
| 46 | if (typeof raw !== "object" || raw === null || Array.isArray(raw)) { |
| 47 | throw new Error("models.dev response is not an object"); |
| 48 | } |
| 49 | |
| 50 | const out: ModelsDevApi = {}; |
| 51 | let skippedModels = 0; |
| 52 | |
| 53 | for (const [providerId, providerRaw] of Object.entries( |
| 54 | raw as Record<string, unknown>, |
| 55 | )) { |
| 56 | if ( |
| 57 | typeof providerRaw !== "object" || |
| 58 | providerRaw === null || |
| 59 | Array.isArray(providerRaw) |
| 60 | ) { |
| 61 | Logger.warn( |
| 62 | `models.dev: skipping provider "${providerId}" (not an object)`, |
| 63 | ); |
| 64 | continue; |
| 65 | } |
| 66 | const providerObj = providerRaw as { |
| 67 | id?: unknown; |
| 68 | name?: unknown; |
| 69 | models?: unknown; |
| 70 | }; |
| 71 | if ( |
| 72 | typeof providerObj.models !== "object" || |
| 73 | providerObj.models === null || |
| 74 | Array.isArray(providerObj.models) |
| 75 | ) { |
| 76 | Logger.warn(`models.dev: skipping provider "${providerId}" (no models)`); |
| 77 | continue; |
| 78 | } |
| 79 | |
| 80 | const models: Record<string, ModelsDevModel> = {}; |
| 81 | for (const [modelId, modelRaw] of Object.entries( |
| 82 | providerObj.models as Record<string, unknown>, |
| 83 | )) { |
| 84 | const parsed = ModelsDevModelSchema.safeParse(modelRaw); |
| 85 | if (parsed.success) { |
| 86 | models[modelId] = parsed.data; |
| 87 | } else { |
| 88 | skippedModels += 1; |
| 89 | Logger.warn( |
| 90 | `models.dev: skipping ${providerId}/${modelId} (${parsed.error.issues[0]?.message ?? "validation error"})`, |
| 91 | ); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | out[providerId] = { |
| 96 | id: typeof providerObj.id === "string" ? providerObj.id : providerId, |
| 97 | name: typeof providerObj.name === "string" ? providerObj.name : undefined, |
| 98 | models, |
| 99 | }; |
| 100 | } |
| 101 | |
| 102 | if (skippedModels > 0) { |
no test coverage detected
searching dependent graphs…