@ai-matrx/agents 0.59.267 → 0.59.269

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/catalog/index.d.cts +2 -1
  3. package/dist/catalog/index.d.ts +2 -1
  4. package/dist/catalog/native/index.d.cts +2 -1
  5. package/dist/catalog/native/index.d.ts +2 -1
  6. package/dist/catalog/react/index.d.cts +2 -1
  7. package/dist/catalog/react/index.d.ts +2 -1
  8. package/dist/content-transfer/index.d.cts +2 -1
  9. package/dist/content-transfer/index.d.ts +2 -1
  10. package/dist/content-transfer/react/index.d.cts +2 -1
  11. package/dist/content-transfer/react/index.d.ts +2 -1
  12. package/dist/context/index.d.cts +2 -1
  13. package/dist/context/index.d.ts +2 -1
  14. package/dist/context/react/index.cjs +12 -1
  15. package/dist/context/react/index.d.cts +2 -1
  16. package/dist/context/react/index.d.ts +2 -1
  17. package/dist/context/react/index.js +12 -1
  18. package/dist/envelope/index.d.cts +2 -1
  19. package/dist/envelope/index.d.ts +2 -1
  20. package/dist/field-flags/index.d.cts +2 -1
  21. package/dist/field-flags/index.d.ts +2 -1
  22. package/dist/generated/llm-enums.d.cts +2 -1
  23. package/dist/generated/llm-enums.d.ts +2 -1
  24. package/dist/generated/provision-offers.d.cts +2 -1
  25. package/dist/generated/provision-offers.d.ts +2 -1
  26. package/dist/generated/source-attribution.d.cts +2 -1
  27. package/dist/generated/source-attribution.d.ts +2 -1
  28. package/dist/generated/stream-events.d.cts +2 -1
  29. package/dist/generated/stream-events.d.ts +2 -1
  30. package/dist/generated/workflow-events.d.cts +2 -1
  31. package/dist/generated/workflow-events.d.ts +2 -1
  32. package/dist/index.d.cts +2 -1
  33. package/dist/index.d.ts +2 -1
  34. package/dist/intelligence/index.d.cts +2 -1
  35. package/dist/intelligence/index.d.ts +2 -1
  36. package/dist/intelligence/react/index.d.cts +2 -1
  37. package/dist/intelligence/react/index.d.ts +2 -1
  38. package/dist/mandates/index.d.cts +2 -1
  39. package/dist/mandates/index.d.ts +2 -1
  40. package/dist/mandates/runs.d.cts +2 -1
  41. package/dist/mandates/runs.d.ts +2 -1
  42. package/dist/message-parts/index.d.cts +2 -1
  43. package/dist/message-parts/index.d.ts +2 -1
  44. package/dist/models/index.cjs +364 -238
  45. package/dist/models/index.d.cts +141 -14
  46. package/dist/models/index.d.ts +141 -14
  47. package/dist/models/index.js +364 -238
  48. package/dist/models/react/index.d.cts +11 -2
  49. package/dist/models/react/index.d.ts +11 -2
  50. package/dist/portable/index.d.cts +4 -3
  51. package/dist/portable/index.d.ts +4 -3
  52. package/dist/portable/mcp.d.cts +3 -2
  53. package/dist/portable/mcp.d.ts +3 -2
  54. package/dist/presentation/decision-answers.d.cts +2 -1
  55. package/dist/presentation/decision-answers.d.ts +2 -1
  56. package/dist/projection/request.d.cts +2 -1
  57. package/dist/projection/request.d.ts +2 -1
  58. package/dist/projection/workflow.d.cts +2 -1
  59. package/dist/projection/workflow.d.ts +2 -1
  60. package/dist/quick/index.d.cts +2 -1
  61. package/dist/quick/index.d.ts +2 -1
  62. package/dist/react/index.d.cts +2 -1
  63. package/dist/react/index.d.ts +2 -1
  64. package/dist/runtime/index.d.cts +2 -1
  65. package/dist/runtime/index.d.ts +2 -1
  66. package/dist/settings/index.d.cts +2 -1
  67. package/dist/settings/index.d.ts +2 -1
  68. package/dist/settings/react/index.d.cts +2 -1
  69. package/dist/settings/react/index.d.ts +2 -1
  70. package/dist/skills/index.d.cts +2 -1
  71. package/dist/skills/index.d.ts +2 -1
  72. package/dist/skills/react/index.d.cts +2 -1
  73. package/dist/skills/react/index.d.ts +2 -1
  74. package/dist/sources/index.d.cts +2 -1
  75. package/dist/sources/index.d.ts +2 -1
  76. package/dist/sources/react/index.d.cts +2 -1
  77. package/dist/sources/react/index.d.ts +2 -1
  78. package/dist/sources/runtime/index.d.cts +2 -1
  79. package/dist/sources/runtime/index.d.ts +2 -1
  80. package/dist/tools/index.d.cts +2 -1
  81. package/dist/tools/index.d.ts +2 -1
  82. package/dist/{types.generated-BDphyhqo.d.cts → types.generated-BP2_h0te.d.cts} +2 -1
  83. package/dist/{types.generated-BDphyhqo.d.ts → types.generated-BP2_h0te.d.ts} +2 -1
  84. package/dist/versions/index.d.cts +2 -1
  85. package/dist/versions/index.d.ts +2 -1
  86. package/dist/versions/react/index.d.cts +2 -1
  87. package/dist/versions/react/index.d.ts +2 -1
  88. package/package.json +2 -2
  89. package/scripts/check-model-catalog-reads.mjs +11 -6
@@ -23,6 +23,10 @@ __export(models_exports, {
23
23
  CHAT_DEFAULT_MODEL_KNOB: () => CHAT_DEFAULT_MODEL_KNOB,
24
24
  CONTENT_TYPES: () => CONTENT_TYPES,
25
25
  DEFAULT_CAPABILITIES: () => DEFAULT_CAPABILITIES,
26
+ DEVICE_FILE_USABLE_STATES: () => DEVICE_FILE_USABLE_STATES,
27
+ DEVICE_OWN_FILE_USABLE_STATES: () => DEVICE_OWN_FILE_USABLE_STATES,
28
+ DEVICE_SERVED_VIA: () => DEVICE_SERVED_VIA,
29
+ DeviceModelUnavailableError: () => DeviceModelUnavailableError,
26
30
  EMPTY_MODEL_RECORDS: () => EMPTY_MODEL_RECORDS,
27
31
  EXCLUDED_REVIEW_STATES: () => EXCLUDED_REVIEW_STATES,
28
32
  FEATURE_BUCKETS: () => FEATURE_BUCKETS,
@@ -43,6 +47,7 @@ __export(models_exports, {
43
47
  TOP_TIER_LOCKED_TITLE: () => TOP_TIER_LOCKED_TITLE,
44
48
  acceptsTextInstructions: () => acceptsTextInstructions,
45
49
  announceModelCatalogChanged: () => announceModelCatalogChanged,
50
+ availableOnDevices: () => availableOnDevices,
46
51
  blendedPrice: () => blendedPrice,
47
52
  buildModelClassIndex: () => buildModelClassIndex,
48
53
  buildSuccessionIndex: () => buildSuccessionIndex,
@@ -112,6 +117,7 @@ __export(models_exports, {
112
117
  quantFromName: () => quantFromName,
113
118
  rankVariants: () => rankVariants,
114
119
  readCuratedRows: () => readCuratedRows,
120
+ readDeviceModelFiles: () => readDeviceModelFiles,
115
121
  readGgufHeader: () => readGgufHeader,
116
122
  readModelCatalog: () => readModelCatalog,
117
123
  readModelIdentities: () => readModelIdentities,
@@ -119,8 +125,10 @@ __export(models_exports, {
119
125
  reconcileModelFavoriteIds: () => reconcileModelFavoriteIds,
120
126
  reportModelDiagnostic: () => reportModelDiagnostic,
121
127
  requireCanonicalCapabilities: () => requireCanonicalCapabilities,
128
+ requiredArtifactIds: () => requiredArtifactIds,
122
129
  requiredArtifacts: () => requiredArtifacts,
123
130
  resetSuccessionLogForTests: () => resetSuccessionLogForTests,
131
+ resolveDeviceModel: () => resolveDeviceModel,
124
132
  resolveLiveModelId: () => resolveLiveModelId,
125
133
  resolveModelMapKnob: () => resolveModelMapKnob,
126
134
  resolveNewAgentModelId: () => resolveNewAgentModelId,
@@ -159,6 +167,7 @@ __export(models_exports, {
159
167
  splitSetProblems: () => splitSetProblems,
160
168
  takesInput: () => takesInput,
161
169
  toAuditRecord: () => toAuditRecord,
170
+ toDeviceModelFile: () => toDeviceModelFile,
162
171
  validateModelSet: () => validateModelSet,
163
172
  variantVerdict: () => variantVerdict,
164
173
  withModelClass: () => withModelClass
@@ -414,13 +423,13 @@ function setModelDiagnosticsSink(sink) {
414
423
  }
415
424
  function reportModelDiagnostic(dedupeKey, entry) {
416
425
  try {
417
- const s3 = state();
418
- if (s3.reported.has(dedupeKey)) return;
419
- s3.reported.add(dedupeKey);
426
+ const s4 = state();
427
+ if (s4.reported.has(dedupeKey)) return;
428
+ s4.reported.add(dedupeKey);
420
429
  console.warn(`[parseCapabilities] ${entry.message}`);
421
- if (s3.sink) s3.sink(entry);
422
- else if (!s3.announced) {
423
- s3.announced = true;
430
+ if (s4.sink) s4.sink(entry);
431
+ else if (!s4.announced) {
432
+ s4.announced = true;
424
433
  console.error(
425
434
  "[@ai-matrx/agents/models] No diagnostics sink is bound \u2014 capability-vocabulary drift reaches the console only. Call setModelDiagnosticsSink(...) with your error store."
426
435
  );
@@ -1093,13 +1102,13 @@ function createModelRecords(config) {
1093
1102
  if (known.fetchScope === "active" || known.fetchScope === "all") {
1094
1103
  jobs.push(
1095
1104
  readOptions().then(
1096
- (rows3) => update((s3) => {
1097
- const entities = { ...s3.entities };
1105
+ (rows3) => update((s4) => {
1106
+ const entities = { ...s4.entities };
1098
1107
  for (const row of rows3) {
1099
1108
  const existing = entities[row.id];
1100
1109
  entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: existing?._fetchType === "full" ? "full" : "options" };
1101
1110
  }
1102
- return withIdLists({ ...s3, entities, lastFetched: Date.now(), error: null });
1111
+ return withIdLists({ ...s4, entities, lastFetched: Date.now(), error: null });
1103
1112
  }),
1104
1113
  (err) => fail("model-records-refresh-options-failed", messageOf(err))
1105
1114
  )
@@ -1109,9 +1118,9 @@ function createModelRecords(config) {
1109
1118
  if (record._fetchType !== "full") continue;
1110
1119
  jobs.push(
1111
1120
  readFullModel(id).then(
1112
- (fresh) => update((s3) => {
1113
- const existing = s3.entities[fresh.id];
1114
- return withIdLists({ ...s3, entities: { ...s3.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
1121
+ (fresh) => update((s4) => {
1122
+ const existing = s4.entities[fresh.id];
1123
+ return withIdLists({ ...s4, entities: { ...s4.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
1115
1124
  }),
1116
1125
  (err) => fail("model-records-refresh-model-failed", `${id}: ${messageOf(err)}`, { modelId: id })
1117
1126
  )
@@ -1122,7 +1131,7 @@ function createModelRecords(config) {
1122
1131
  Promise.resolve(ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(id), id).maybeSingle()).then(
1123
1132
  ({ data, error }) => {
1124
1133
  if (error) throw error;
1125
- if (data) update((s3) => ({ ...s3, identityById: { ...s3.identityById, [id]: data } }));
1134
+ if (data) update((s4) => ({ ...s4, identityById: { ...s4.identityById, [id]: data } }));
1126
1135
  }
1127
1136
  ).catch((err) => fail("model-records-refresh-identity-failed", `${id}: ${messageOf(err)}`, { modelId: id }))
1128
1137
  );
@@ -1130,7 +1139,7 @@ function createModelRecords(config) {
1130
1139
  if (known.modelClassStatus === "succeeded") {
1131
1140
  jobs.push(
1132
1141
  readOfferings().then(
1133
- (rows3) => update((s3) => ({ ...s3, modelClassIndex: buildModelClassIndex(rows3) })),
1142
+ (rows3) => update((s4) => ({ ...s4, modelClassIndex: buildModelClassIndex(rows3) })),
1134
1143
  (err) => fail("model-records-refresh-classes-failed", messageOf(err))
1135
1144
  )
1136
1145
  );
@@ -1138,11 +1147,11 @@ function createModelRecords(config) {
1138
1147
  for (const id of classesReadFor) {
1139
1148
  jobs.push(
1140
1149
  readOfferings(id).then(
1141
- (rows3) => update((s3) => {
1142
- if (s3.modelClassStatus === "succeeded") return s3;
1150
+ (rows3) => update((s4) => {
1151
+ if (s4.modelClassStatus === "succeeded") return s4;
1143
1152
  const slice = buildModelClassIndex(rows3);
1144
- const base = s3.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1145
- return { ...s3, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
1153
+ const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1154
+ return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
1146
1155
  }),
1147
1156
  (err) => fail("model-records-refresh-classes-failed", `${id}: ${messageOf(err)}`, { modelId: id })
1148
1157
  )
@@ -1156,7 +1165,7 @@ function createModelRecords(config) {
1156
1165
  ({ data, error }) => {
1157
1166
  if (error) throw error;
1158
1167
  const next = data && typeof data === "object" && !Array.isArray(data) ? data : null;
1159
- update((s3) => ({ ...s3, classConfigByOffering: { ...s3.classConfigByOffering, [key]: { modelId: cfg.modelId, controls: next?.controls ?? null, constraints: next?.constraints ?? null } } }));
1168
+ update((s4) => ({ ...s4, classConfigByOffering: { ...s4.classConfigByOffering, [key]: { modelId: cfg.modelId, controls: next?.controls ?? null, constraints: next?.constraints ?? null } } }));
1160
1169
  }
1161
1170
  ).catch((err) => fail("model-records-refresh-class-config-failed", `${key}: ${messageOf(err)}`))
1162
1171
  );
@@ -1202,29 +1211,29 @@ function createModelRecords(config) {
1202
1211
  loadOptions() {
1203
1212
  if (state2.fetchScope === "active" || state2.fetchScope === "all") return Promise.resolve();
1204
1213
  return once("options", async () => {
1205
- update((s3) => ({ ...s3, isLoading: true, error: null }));
1214
+ update((s4) => ({ ...s4, isLoading: true, error: null }));
1206
1215
  try {
1207
1216
  const rows3 = await readOptions();
1208
- update((s3) => {
1209
- const entities = { ...s3.entities };
1217
+ update((s4) => {
1218
+ const entities = { ...s4.entities };
1210
1219
  for (const row of rows3) {
1211
1220
  const existing = entities[row.id];
1212
1221
  if (existing?._fetchType === "full") continue;
1213
1222
  entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: "options" };
1214
1223
  }
1215
1224
  return withIdLists({
1216
- ...s3,
1225
+ ...s4,
1217
1226
  entities,
1218
1227
  isLoading: false,
1219
1228
  error: null,
1220
- fetchScope: s3.fetchScope !== "all" && s3.fetchScope !== "deprecated" ? "active" : s3.fetchScope,
1229
+ fetchScope: s4.fetchScope !== "all" && s4.fetchScope !== "deprecated" ? "active" : s4.fetchScope,
1221
1230
  lastFetched: Date.now()
1222
1231
  });
1223
1232
  });
1224
1233
  } catch (err) {
1225
1234
  const message = messageOf(err);
1226
1235
  errorSink({ code: "model-records-options-load-failed", message });
1227
- update((s3) => ({ ...s3, isLoading: false, error: message }));
1236
+ update((s4) => ({ ...s4, isLoading: false, error: message }));
1228
1237
  }
1229
1238
  });
1230
1239
  },
@@ -1234,23 +1243,23 @@ function createModelRecords(config) {
1234
1243
  return inFlight.get(`model:${modelId}`) ?? Promise.resolve();
1235
1244
  }
1236
1245
  return once(`model:${modelId}`, async () => {
1237
- update((s3) => {
1238
- const detailErrorById = { ...s3.detailErrorById };
1246
+ update((s4) => {
1247
+ const detailErrorById = { ...s4.detailErrorById };
1239
1248
  delete detailErrorById[modelId];
1240
- return { ...s3, detailStatusById: { ...s3.detailStatusById, [modelId]: "loading" }, detailErrorById };
1249
+ return { ...s4, detailStatusById: { ...s4.detailStatusById, [modelId]: "loading" }, detailErrorById };
1241
1250
  });
1242
1251
  try {
1243
1252
  const record = await readFullModel(modelId);
1244
- update((s3) => {
1245
- const existing = s3.entities[record.id];
1246
- const detailErrorById = { ...s3.detailErrorById };
1253
+ update((s4) => {
1254
+ const existing = s4.entities[record.id];
1255
+ const detailErrorById = { ...s4.detailErrorById };
1247
1256
  delete detailErrorById[modelId];
1248
1257
  return withIdLists({
1249
- ...s3,
1250
- detailStatusById: { ...s3.detailStatusById, [modelId]: "succeeded" },
1258
+ ...s4,
1259
+ detailStatusById: { ...s4.detailStatusById, [modelId]: "succeeded" },
1251
1260
  detailErrorById,
1252
1261
  entities: {
1253
- ...s3.entities,
1262
+ ...s4.entities,
1254
1263
  [record.id]: { ...emptyRecord(), ...existing, ...record, maker: record.maker ?? existing?.maker ?? null, _fetchType: "full" }
1255
1264
  }
1256
1265
  });
@@ -1258,21 +1267,21 @@ function createModelRecords(config) {
1258
1267
  } catch (err) {
1259
1268
  const message = `Model configuration ${modelId}: ${messageOf(err)}`;
1260
1269
  errorSink({ code: "model-records-model-load-failed", message, context: { modelId } });
1261
- update((s3) => ({
1262
- ...s3,
1263
- detailStatusById: { ...s3.detailStatusById, [modelId]: "failed" },
1264
- detailErrorById: { ...s3.detailErrorById, [modelId]: message }
1270
+ update((s4) => ({
1271
+ ...s4,
1272
+ detailStatusById: { ...s4.detailStatusById, [modelId]: "failed" },
1273
+ detailErrorById: { ...s4.detailErrorById, [modelId]: message }
1265
1274
  }));
1266
1275
  }
1267
1276
  });
1268
1277
  },
1269
1278
  retryModel(modelId) {
1270
- update((s3) => {
1271
- const detailStatusById = { ...s3.detailStatusById };
1272
- const detailErrorById = { ...s3.detailErrorById };
1279
+ update((s4) => {
1280
+ const detailStatusById = { ...s4.detailStatusById };
1281
+ const detailErrorById = { ...s4.detailErrorById };
1273
1282
  delete detailStatusById[modelId];
1274
1283
  delete detailErrorById[modelId];
1275
- return { ...s3, detailStatusById, detailErrorById };
1284
+ return { ...s4, detailStatusById, detailErrorById };
1276
1285
  });
1277
1286
  },
1278
1287
  loadClassConfig(modelId, offeringId) {
@@ -1280,37 +1289,37 @@ function createModelRecords(config) {
1280
1289
  const status = state2.classConfigStatusByOffering[key];
1281
1290
  if (status === "loading" || status === "succeeded") return inFlight.get(`class:${key}`) ?? Promise.resolve();
1282
1291
  return once(`class:${key}`, async () => {
1283
- update((s3) => ({ ...s3, classConfigStatusByOffering: { ...s3.classConfigStatusByOffering, [key]: "loading" } }));
1292
+ update((s4) => ({ ...s4, classConfigStatusByOffering: { ...s4.classConfigStatusByOffering, [key]: "loading" } }));
1284
1293
  try {
1285
1294
  const { data, error } = await ai().rpc("resolve_model_config", { p_model_id: modelId, p_offering_id: offeringId });
1286
1295
  if (error) throw error;
1287
1296
  const cfg = data && typeof data === "object" && !Array.isArray(data) ? data : null;
1288
- update((s3) => ({
1289
- ...s3,
1290
- classConfigStatusByOffering: { ...s3.classConfigStatusByOffering, [key]: "succeeded" },
1297
+ update((s4) => ({
1298
+ ...s4,
1299
+ classConfigStatusByOffering: { ...s4.classConfigStatusByOffering, [key]: "succeeded" },
1291
1300
  classConfigByOffering: {
1292
- ...s3.classConfigByOffering,
1301
+ ...s4.classConfigByOffering,
1293
1302
  [key]: { modelId, controls: cfg?.controls ?? null, constraints: cfg?.constraints ?? null }
1294
1303
  }
1295
1304
  }));
1296
1305
  } catch (err) {
1297
1306
  errorSink({ code: "model-records-class-config-failed", message: `Model class configuration ${modelId} / ${offeringId}: ${messageOf(err)}` });
1298
- update((s3) => ({ ...s3, classConfigStatusByOffering: { ...s3.classConfigStatusByOffering, [key]: "failed" } }));
1307
+ update((s4) => ({ ...s4, classConfigStatusByOffering: { ...s4.classConfigStatusByOffering, [key]: "failed" } }));
1299
1308
  }
1300
1309
  });
1301
1310
  },
1302
1311
  loadClasses() {
1303
1312
  if (state2.modelClassStatus === "loading" || state2.modelClassStatus === "succeeded") return inFlight.get("classes") ?? Promise.resolve();
1304
1313
  return once("classes", async () => {
1305
- update((s3) => ({ ...s3, modelClassStatus: "loading" }));
1314
+ update((s4) => ({ ...s4, modelClassStatus: "loading" }));
1306
1315
  try {
1307
1316
  const { data, error } = await ai().from("model_offering").select("model_id, offering_id, served_via, served_via_endpoint_id, priority");
1308
1317
  if (error) throw error;
1309
1318
  const index = buildModelClassIndex(data ?? []);
1310
- update((s3) => ({ ...s3, modelClassStatus: "succeeded", modelClassIndex: index }));
1319
+ update((s4) => ({ ...s4, modelClassStatus: "succeeded", modelClassIndex: index }));
1311
1320
  } catch (err) {
1312
1321
  errorSink({ code: "model-records-classes-failed", message: `Model classes: ${messageOf(err)}` });
1313
- update((s3) => ({ ...s3, modelClassStatus: "failed" }));
1322
+ update((s4) => ({ ...s4, modelClassStatus: "failed" }));
1314
1323
  }
1315
1324
  });
1316
1325
  },
@@ -1325,11 +1334,11 @@ function createModelRecords(config) {
1325
1334
  if (error) throw error;
1326
1335
  const slice = buildModelClassIndex(data ?? []);
1327
1336
  classesReadFor.add(modelId);
1328
- update((s3) => {
1329
- if (s3.modelClassStatus === "succeeded") return s3;
1330
- const base = s3.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1337
+ update((s4) => {
1338
+ if (s4.modelClassStatus === "succeeded") return s4;
1339
+ const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1331
1340
  return {
1332
- ...s3,
1341
+ ...s4,
1333
1342
  modelClassIndex: {
1334
1343
  classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId },
1335
1344
  classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering }
@@ -1347,20 +1356,20 @@ function createModelRecords(config) {
1347
1356
  return inFlight.get(`identity:${modelId}`) ?? Promise.resolve();
1348
1357
  }
1349
1358
  return once(`identity:${modelId}`, async () => {
1350
- update((s3) => ({ ...s3, identityStatusById: { ...s3.identityStatusById, [modelId]: "loading" } }));
1359
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "loading" } }));
1351
1360
  try {
1352
1361
  const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(modelId), modelId).maybeSingle();
1353
1362
  if (error) throw error;
1354
1363
  if (!data) throw new Error(`AI model ${modelId} is not available (ai.model_definition has no such row)`);
1355
1364
  const row = data;
1356
- update((s3) => ({
1357
- ...s3,
1358
- identityById: { ...s3.identityById, [row.id]: row, [modelId]: row },
1359
- identityStatusById: { ...s3.identityStatusById, [row.id]: "succeeded", [modelId]: "succeeded" }
1365
+ update((s4) => ({
1366
+ ...s4,
1367
+ identityById: { ...s4.identityById, [row.id]: row, [modelId]: row },
1368
+ identityStatusById: { ...s4.identityStatusById, [row.id]: "succeeded", [modelId]: "succeeded" }
1360
1369
  }));
1361
1370
  } catch (err) {
1362
1371
  errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelId } });
1363
- update((s3) => ({ ...s3, identityStatusById: { ...s3.identityStatusById, [modelId]: "failed" } }));
1372
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "failed" } }));
1364
1373
  }
1365
1374
  });
1366
1375
  },
@@ -1379,26 +1388,26 @@ function createModelRecords(config) {
1379
1388
  }
1380
1389
  if (wanted.length > 0) {
1381
1390
  const marked = Object.fromEntries(wanted.map((id) => [id, "loading"]));
1382
- update((s3) => ({ ...s3, identityStatusById: { ...s3.identityStatusById, ...marked } }));
1391
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...marked } }));
1383
1392
  const run = (async () => {
1384
1393
  try {
1385
1394
  const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").in("id", wanted);
1386
1395
  if (error) throw error;
1387
1396
  const rows3 = Array.isArray(data) ? data : [];
1388
1397
  const got = new Set(rows3.map((r) => r.id));
1389
- update((s3) => ({
1390
- ...s3,
1391
- identityById: { ...s3.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
1398
+ update((s4) => ({
1399
+ ...s4,
1400
+ identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
1392
1401
  identityStatusById: {
1393
- ...s3.identityStatusById,
1402
+ ...s4.identityStatusById,
1394
1403
  ...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"]))
1395
1404
  }
1396
1405
  }));
1397
1406
  } catch (err) {
1398
1407
  errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
1399
- update((s3) => ({
1400
- ...s3,
1401
- identityStatusById: { ...s3.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) }
1408
+ update((s4) => ({
1409
+ ...s4,
1410
+ identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) }
1402
1411
  }));
1403
1412
  }
1404
1413
  })();
@@ -1416,11 +1425,11 @@ function createModelRecords(config) {
1416
1425
  if (!classesReadFor.has(modelId) && state2.modelClassStatus !== "succeeded") {
1417
1426
  const slice = buildModelClassIndex(offerings.filter((o) => o.model_id === modelId));
1418
1427
  classesReadFor.add(modelId);
1419
- update((s3) => {
1420
- if (s3.modelClassStatus === "succeeded") return s3;
1421
- const base = s3.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1428
+ update((s4) => {
1429
+ if (s4.modelClassStatus === "succeeded") return s4;
1430
+ const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1422
1431
  return {
1423
- ...s3,
1432
+ ...s4,
1424
1433
  modelClassIndex: {
1425
1434
  classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId },
1426
1435
  classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering }
@@ -1439,16 +1448,16 @@ function createModelRecords(config) {
1439
1448
  controls: row.controls ?? null,
1440
1449
  capabilities: requireCanonicalCapabilities(row.capabilities, { modelId: row.id, modelName: row.name })
1441
1450
  });
1442
- update((s3) => {
1443
- const existing = s3.entities[record.id];
1444
- const detailErrorById = { ...s3.detailErrorById };
1451
+ update((s4) => {
1452
+ const existing = s4.entities[record.id];
1453
+ const detailErrorById = { ...s4.detailErrorById };
1445
1454
  delete detailErrorById[modelId];
1446
1455
  return withIdLists({
1447
- ...s3,
1448
- detailStatusById: { ...s3.detailStatusById, [modelId]: "succeeded" },
1456
+ ...s4,
1457
+ detailStatusById: { ...s4.detailStatusById, [modelId]: "succeeded" },
1449
1458
  detailErrorById,
1450
1459
  entities: {
1451
- ...s3.entities,
1460
+ ...s4.entities,
1452
1461
  [record.id]: { ...emptyRecord(), ...existing, ...record, maker: record.maker ?? existing?.maker ?? null, _fetchType: "full" }
1453
1462
  }
1454
1463
  });
@@ -1458,14 +1467,14 @@ function createModelRecords(config) {
1458
1467
  }
1459
1468
  },
1460
1469
  hydrate({ models, fetchType, fetchScope, lastFetched }) {
1461
- update((s3) => {
1462
- const entities = { ...s3.entities };
1470
+ update((s4) => {
1471
+ const entities = { ...s4.entities };
1463
1472
  for (const raw of models) {
1464
1473
  const existing = entities[raw.id];
1465
1474
  if (existing?._fetchType === "full" && fetchType === "options") continue;
1466
1475
  entities[raw.id] = { ...emptyRecord(), ...normalizeModel({ ...raw, controls: raw.controls ?? null }), _fetchType: fetchType };
1467
1476
  }
1468
- return withIdLists({ ...s3, entities, fetchScope, lastFetched, isLoading: false, error: null });
1477
+ return withIdLists({ ...s4, entities, fetchScope, lastFetched, isLoading: false, error: null });
1469
1478
  });
1470
1479
  }
1471
1480
  };
@@ -1485,28 +1494,28 @@ function memo(fn) {
1485
1494
  var UNSET = /* @__PURE__ */ Symbol("unset");
1486
1495
  var activeKey = null;
1487
1496
  var activeOut = [];
1488
- function selectActiveModels(s3) {
1489
- if (!activeKey || activeKey[0] !== s3.entities || activeKey[1] !== s3.activeIds) {
1490
- activeKey = [s3.entities, s3.activeIds];
1491
- activeOut = s3.activeIds.map((id) => s3.entities[id]).filter((m) => !!m);
1497
+ function selectActiveModels(s4) {
1498
+ if (!activeKey || activeKey[0] !== s4.entities || activeKey[1] !== s4.activeIds) {
1499
+ activeKey = [s4.entities, s4.activeIds];
1500
+ activeOut = s4.activeIds.map((id) => s4.entities[id]).filter((m) => !!m);
1492
1501
  }
1493
1502
  return activeOut;
1494
1503
  }
1495
1504
  var deprecatedKey = null;
1496
1505
  var deprecatedOut = [];
1497
- function selectDeprecatedModels(s3) {
1498
- if (!deprecatedKey || deprecatedKey[0] !== s3.entities || deprecatedKey[1] !== s3.deprecatedIds) {
1499
- deprecatedKey = [s3.entities, s3.deprecatedIds];
1500
- deprecatedOut = s3.deprecatedIds.map((id) => s3.entities[id]).filter((m) => !!m);
1506
+ function selectDeprecatedModels(s4) {
1507
+ if (!deprecatedKey || deprecatedKey[0] !== s4.entities || deprecatedKey[1] !== s4.deprecatedIds) {
1508
+ deprecatedKey = [s4.entities, s4.deprecatedIds];
1509
+ deprecatedOut = s4.deprecatedIds.map((id) => s4.entities[id]).filter((m) => !!m);
1501
1510
  }
1502
1511
  return deprecatedOut;
1503
1512
  }
1504
1513
  var allOf = memo((entities) => Object.values(entities));
1505
- var selectAllModels = (s3) => allOf(s3.entities);
1514
+ var selectAllModels = (s4) => allOf(s4.entities);
1506
1515
  var optionsOf = memo(
1507
1516
  (models) => models.map((m) => ({ value: m.id, label: modelDisplayLabel(m) || m.id, maker: m.maker, costRating: m.cost_rating, speedRating: m.speed_rating }))
1508
1517
  );
1509
- var selectModelOptions = (s3) => optionsOf(selectActiveModels(s3));
1518
+ var selectModelOptions = (s4) => optionsOf(selectActiveModels(s4));
1510
1519
  var allOptionsOf = memo(
1511
1520
  (entities) => Object.values(entities).map((m) => ({
1512
1521
  value: m.id,
@@ -1517,30 +1526,30 @@ var allOptionsOf = memo(
1517
1526
  successorId: m.successor_id ?? null
1518
1527
  }))
1519
1528
  );
1520
- var selectAllModelOptions = (s3) => allOptionsOf(s3.entities);
1521
- var selectModelById = (s3, modelId) => modelId ? s3.entities[modelId] : void 0;
1522
- function selectModelLabelById(s3, modelId) {
1529
+ var selectAllModelOptions = (s4) => allOptionsOf(s4.entities);
1530
+ var selectModelById = (s4, modelId) => modelId ? s4.entities[modelId] : void 0;
1531
+ function selectModelLabelById(s4, modelId) {
1523
1532
  if (!modelId) return void 0;
1524
- const m = s3.entities[modelId];
1525
- const identity = s3.identityById[modelId];
1533
+ const m = s4.entities[modelId];
1534
+ const identity = s4.identityById[modelId];
1526
1535
  return modelDisplayLabel(m) || modelDisplayLabel(identity);
1527
1536
  }
1528
- var selectModelNameById = (s3, modelId) => modelId ? s3.entities[modelId]?.name : void 0;
1529
- var selectModelClassName = (s3, modelId, offeringId) => modelClassName(s3.modelClassIndex, modelId, offeringId);
1530
- function selectModelLabelWithClass(s3, modelId, offeringId) {
1531
- const label = selectModelLabelById(s3, modelId);
1532
- return label ? withModelClass(label, selectModelClassName(s3, modelId, offeringId)) : void 0;
1533
- }
1534
- var selectModelControls = (s3, modelId) => s3.entities[modelId]?.controls;
1535
- var selectModelFetchType = (s3, modelId) => s3.entities[modelId]?._fetchType;
1536
- var selectModelFullyLoaded = (s3, modelId) => !!modelId && s3.entities[modelId]?._fetchType === "full";
1537
- var selectModelDetailError = (s3, modelId) => modelId && s3.detailErrorById[modelId] || null;
1538
- var selectModelClassConfig = (s3, modelId, offeringId) => modelId && offeringId ? s3.classConfigByOffering[classConfigKey(modelId, offeringId)] : void 0;
1539
- var selectActiveModelsReady = (s3) => (s3.fetchScope === "active" || s3.fetchScope === "all") && s3.activeIds.length > 0;
1540
- var selectDeprecatedModelsReady = (s3) => (s3.fetchScope === "deprecated" || s3.fetchScope === "all") && s3.deprecatedIds.length > 0;
1541
- var selectAllModelsReady = (s3) => s3.fetchScope === "all";
1542
- var selectModelIdentityById = (s3, modelId) => s3.identityById[modelId];
1543
- var selectModelIdentityLookupStatus = (s3, modelId) => s3.identityStatusById[modelId] ?? "idle";
1537
+ var selectModelNameById = (s4, modelId) => modelId ? s4.entities[modelId]?.name : void 0;
1538
+ var selectModelClassName = (s4, modelId, offeringId) => modelClassName(s4.modelClassIndex, modelId, offeringId);
1539
+ function selectModelLabelWithClass(s4, modelId, offeringId) {
1540
+ const label = selectModelLabelById(s4, modelId);
1541
+ return label ? withModelClass(label, selectModelClassName(s4, modelId, offeringId)) : void 0;
1542
+ }
1543
+ var selectModelControls = (s4, modelId) => s4.entities[modelId]?.controls;
1544
+ var selectModelFetchType = (s4, modelId) => s4.entities[modelId]?._fetchType;
1545
+ var selectModelFullyLoaded = (s4, modelId) => !!modelId && s4.entities[modelId]?._fetchType === "full";
1546
+ var selectModelDetailError = (s4, modelId) => modelId && s4.detailErrorById[modelId] || null;
1547
+ var selectModelClassConfig = (s4, modelId, offeringId) => modelId && offeringId ? s4.classConfigByOffering[classConfigKey(modelId, offeringId)] : void 0;
1548
+ var selectActiveModelsReady = (s4) => (s4.fetchScope === "active" || s4.fetchScope === "all") && s4.activeIds.length > 0;
1549
+ var selectDeprecatedModelsReady = (s4) => (s4.fetchScope === "deprecated" || s4.fetchScope === "all") && s4.deprecatedIds.length > 0;
1550
+ var selectAllModelsReady = (s4) => s4.fetchScope === "all";
1551
+ var selectModelIdentityById = (s4, modelId) => s4.identityById[modelId];
1552
+ var selectModelIdentityLookupStatus = (s4, modelId) => s4.identityStatusById[modelId] ?? "idle";
1544
1553
  async function readModelIdentities(client, modelIds) {
1545
1554
  const ids = [...new Set(modelIds.filter(Boolean))];
1546
1555
  if (ids.length === 0) return [];
@@ -1840,9 +1849,9 @@ function createModelCatalog(config) {
1840
1849
  };
1841
1850
  },
1842
1851
  load(variant) {
1843
- const s3 = state2[variant];
1844
- if (s3.status === "idle" || s3.status === "error") return run(variant);
1845
- if (s3.status === "ready" && s3.loadedAt !== null && Date.now() - s3.loadedAt > staleAfterMs) return run(variant);
1852
+ const s4 = state2[variant];
1853
+ if (s4.status === "idle" || s4.status === "error") return run(variant);
1854
+ if (s4.status === "ready" && s4.loadedAt !== null && Date.now() - s4.loadedAt > staleAfterMs) return run(variant);
1846
1855
  return inFlight[variant] ?? Promise.resolve();
1847
1856
  },
1848
1857
  reload: (variant) => run(variant)
@@ -2030,9 +2039,9 @@ function placement(v, runtimes, device) {
2030
2039
  }
2031
2040
  return "not_compatible";
2032
2041
  }
2033
- function formatMemoryBytes(n2) {
2034
- const g = n2 / 2 ** 30;
2035
- if (g < 1) return `${Math.max(1, Math.round(n2 / 2 ** 20))} MB`;
2042
+ function formatMemoryBytes(n3) {
2043
+ const g = n3 / 2 ** 30;
2044
+ if (g < 1) return `${Math.max(1, Math.round(n3 / 2 ** 20))} MB`;
2036
2045
  return `${g < 10 ? Math.round(g * 10) / 10 : Math.round(g)} GB`;
2037
2046
  }
2038
2047
  function describe(c, decided, runnerUp, device) {
@@ -2359,41 +2368,41 @@ var Cursor = class {
2359
2368
  this.read = read;
2360
2369
  this.size = size;
2361
2370
  }
2362
- async need(n2) {
2363
- if (this.pos + n2 > this.size) throw new GgufError("the file ends inside its header");
2364
- if (this.pos + n2 > MAX_HEADER) throw new GgufError("its header is larger than any real model's");
2365
- if (this.pos >= this.base && this.pos + n2 <= this.base + this.buf.length) return;
2366
- const len = Math.min(Math.max(n2, CHUNK), this.size - this.pos);
2371
+ async need(n3) {
2372
+ if (this.pos + n3 > this.size) throw new GgufError("the file ends inside its header");
2373
+ if (this.pos + n3 > MAX_HEADER) throw new GgufError("its header is larger than any real model's");
2374
+ if (this.pos >= this.base && this.pos + n3 <= this.base + this.buf.length) return;
2375
+ const len = Math.min(Math.max(n3, CHUNK), this.size - this.pos);
2367
2376
  this.buf = await this.read(this.pos, len);
2368
2377
  this.base = this.pos;
2369
- if (this.buf.length < n2) throw new GgufError("the file ends inside its header");
2378
+ if (this.buf.length < n3) throw new GgufError("the file ends inside its header");
2370
2379
  }
2371
- async view(n2) {
2372
- await this.need(n2);
2373
- const v = new DataView(this.buf.buffer, this.buf.byteOffset + (this.pos - this.base), n2);
2374
- this.pos += n2;
2380
+ async view(n3) {
2381
+ await this.need(n3);
2382
+ const v = new DataView(this.buf.buffer, this.buf.byteOffset + (this.pos - this.base), n3);
2383
+ this.pos += n3;
2375
2384
  return v;
2376
2385
  }
2377
2386
  async u32() {
2378
2387
  return (await this.view(4)).getUint32(0, true);
2379
2388
  }
2380
2389
  async u64() {
2381
- const n2 = (await this.view(8)).getBigUint64(0, true);
2382
- if (n2 > BigInt(Number.MAX_SAFE_INTEGER)) throw new GgufError("a size in its header is out of range");
2383
- return Number(n2);
2390
+ const n3 = (await this.view(8)).getBigUint64(0, true);
2391
+ if (n3 > BigInt(Number.MAX_SAFE_INTEGER)) throw new GgufError("a size in its header is out of range");
2392
+ return Number(n3);
2384
2393
  }
2385
2394
  async str() {
2386
2395
  const len = await this.u64();
2387
2396
  if (len > MAX_STRING) throw new GgufError("a text value in its header is out of range");
2388
2397
  await this.need(len);
2389
2398
  const at = this.pos - this.base;
2390
- const s3 = new TextDecoder().decode(this.buf.subarray(at, at + len));
2399
+ const s4 = new TextDecoder().decode(this.buf.subarray(at, at + len));
2391
2400
  this.pos += len;
2392
- return s3;
2401
+ return s4;
2393
2402
  }
2394
- async skip(n2) {
2395
- if (this.pos + n2 > this.size) throw new GgufError("the file ends inside its header");
2396
- this.pos += n2;
2403
+ async skip(n3) {
2404
+ if (this.pos + n3 > this.size) throw new GgufError("the file ends inside its header");
2405
+ this.pos += n3;
2397
2406
  }
2398
2407
  async scalar(type) {
2399
2408
  switch (type) {
@@ -2445,11 +2454,11 @@ async function readGgufHeader(read, size) {
2445
2454
  const type = await c.u32();
2446
2455
  if (type === 9) {
2447
2456
  const elem = await c.u32();
2448
- const n2 = await c.u64();
2449
- if (n2 > 64 * MAX_COUNT) throw new GgufError("an array in its header is out of range");
2450
- arrays[key] = n2;
2451
- if (elem === 8) for (let j = 0; j < n2; j++) await c.str();
2452
- else if (SCALAR_BYTES[elem] !== void 0) await c.skip(n2 * SCALAR_BYTES[elem]);
2457
+ const n3 = await c.u64();
2458
+ if (n3 > 64 * MAX_COUNT) throw new GgufError("an array in its header is out of range");
2459
+ arrays[key] = n3;
2460
+ if (elem === 8) for (let j = 0; j < n3; j++) await c.str();
2461
+ else if (SCALAR_BYTES[elem] !== void 0) await c.skip(n3 * SCALAR_BYTES[elem]);
2453
2462
  else throw new GgufError(`its header has an array of unknown type ${elem}`);
2454
2463
  } else kv[key] = await c.scalar(type);
2455
2464
  }
@@ -2479,20 +2488,20 @@ async function readGgufHeader(read, size) {
2479
2488
  return { version, kv, array_lengths: arrays, tensors, data_offset: dataOffset, data_end: end };
2480
2489
  }
2481
2490
  function ggufFacts(h) {
2482
- const s3 = (k) => typeof h.kv[k] === "string" ? h.kv[k] : null;
2483
- const n2 = (k) => typeof h.kv[k] === "number" ? h.kv[k] : null;
2484
- const arch = s3("general.architecture");
2485
- const ft = n2("general.file_type");
2491
+ const s4 = (k) => typeof h.kv[k] === "string" ? h.kv[k] : null;
2492
+ const n3 = (k) => typeof h.kv[k] === "number" ? h.kv[k] : null;
2493
+ const arch = s4("general.architecture");
2494
+ const ft = n3("general.file_type");
2486
2495
  const params = h.tensors.reduce((t, x) => t + x.dims.reduce((a, b) => a * b, 1), 0);
2487
2496
  return {
2488
2497
  architecture: arch,
2489
- name: s3("general.name"),
2498
+ name: s4("general.name"),
2490
2499
  quant: ft !== null ? FILE_TYPE_QUANT[ft] ?? `FTYPE_${ft}` : null,
2491
- context_length: arch ? n2(`${arch}.context_length`) : null,
2500
+ context_length: arch ? n3(`${arch}.context_length`) : null,
2492
2501
  parameter_count: params > 0 ? params : null,
2493
- split_count: n2("split.count") ?? 1,
2494
- split_index: n2("split.no") ?? 0,
2495
- type: s3("general.type"),
2502
+ split_count: n3("split.count") ?? 1,
2503
+ split_index: n3("split.no") ?? 0,
2504
+ type: s4("general.type"),
2496
2505
  has_tokenizer: typeof h.kv["tokenizer.ggml.model"] === "string"
2497
2506
  };
2498
2507
  }
@@ -2533,16 +2542,16 @@ var isHelperGguf = (name) => HELPER_RE.test(name);
2533
2542
  var isProjectorName = (name) => name.toLowerCase().includes("mmproj");
2534
2543
  var quantFromName = (name) => QUANT_RE.exec(name.split("/").pop() ?? name)?.[1]?.toUpperCase() ?? null;
2535
2544
  function splitSetProblems(names) {
2536
- const shards = names.map(shardName).filter((s3) => s3 !== null);
2545
+ const shards = names.map(shardName).filter((s4) => s4 !== null);
2537
2546
  if (!shards.length) return [];
2538
- const counts = [...new Set(shards.map((s3) => s3.count))];
2547
+ const counts = [...new Set(shards.map((s4) => s4.count))];
2539
2548
  if (counts.length !== 1) return [`its parts disagree on how many parts there are (${counts.sort((a, b) => a - b).join(", ")})`];
2540
- const n2 = counts[0];
2541
- const have = shards.map((s3) => s3.index);
2542
- const missing = Array.from({ length: n2 }, (_, i) => i + 1).filter((i) => !have.includes(i));
2549
+ const n3 = counts[0];
2550
+ const have = shards.map((s4) => s4.index);
2551
+ const missing = Array.from({ length: n3 }, (_, i) => i + 1).filter((i) => !have.includes(i));
2543
2552
  const dupes = [...new Set(have.filter((i, k) => have.indexOf(i) !== k))].sort((a, b) => a - b);
2544
2553
  const out = [];
2545
- if (missing.length) out.push(`part${missing.length === 1 ? "" : "s"} ${missing.map((i) => `${i} of ${n2}`).join(", ")} ${missing.length === 1 ? "is" : "are"} missing`);
2554
+ if (missing.length) out.push(`part${missing.length === 1 ? "" : "s"} ${missing.map((i) => `${i} of ${n3}`).join(", ")} ${missing.length === 1 ? "is" : "are"} missing`);
2546
2555
  if (dupes.length) out.push(`part${dupes.length === 1 ? "" : "s"} ${dupes.join(", ")} appear${dupes.length === 1 ? "s" : ""} twice`);
2547
2556
  return out;
2548
2557
  }
@@ -2579,8 +2588,8 @@ async function validateModelSet(primary, siblings, env) {
2579
2588
  let parts = [primary];
2580
2589
  if (shard) {
2581
2590
  if (shard.index !== 1) return { ok: false, reason: `This is part ${shard.index} of ${shard.count} of a split model; add its first part, ${shardFileName(shard.stem, 1, shard.count)}.` };
2582
- const same = [primary, ...siblings.filter((s3) => s3 !== primary && shardName(s3.name)?.stem === shard.stem)];
2583
- const problems = splitSetProblems(same.map((s3) => s3.name));
2591
+ const same = [primary, ...siblings.filter((s4) => s4 !== primary && shardName(s4.name)?.stem === shard.stem)];
2592
+ const problems = splitSetProblems(same.map((s4) => s4.name));
2584
2593
  if (problems.length) return { ok: false, reason: `It is a model in ${shard.count} parts and ${problems.join("; ")} (every part has to be in the same folder).` };
2585
2594
  parts = same.sort((a, b) => (shardName(a.name)?.index ?? 0) - (shardName(b.name)?.index ?? 0));
2586
2595
  }
@@ -2613,7 +2622,7 @@ async function validateModelSet(primary, siblings, env) {
2613
2622
  if (end !== null && p.size < end) return { ok: false, reason: `${parts.length > 1 ? `Part ${i + 1} of ${parts.length} is incomplete` : "The file is incomplete"}: it is ${formatMemoryBytes(p.size)} and its header says ${formatMemoryBytes(end)} (a download that stopped early?).` };
2614
2623
  }
2615
2624
  const quant = f.quant ?? quantFromName(primary.name) ?? "unknown";
2616
- const projectorFiles = siblings.filter((s3) => s3 !== primary && extOf(s3.name) === ".gguf" && isProjectorName(s3.name));
2625
+ const projectorFiles = siblings.filter((s4) => s4 !== primary && extOf(s4.name) === ".gguf" && isProjectorName(s4.name));
2617
2626
  const pickedName = pickProjector(quant === "unknown" ? null : quant, projectorFiles.map((p) => p.name));
2618
2627
  let projector = null;
2619
2628
  const picked = projectorFiles.find((p) => p.name === pickedName);
@@ -2735,6 +2744,77 @@ function curatedList(models, { offeredOnly }) {
2735
2744
 
2736
2745
  // models/catalog-reader.ts
2737
2746
  var import_sha256 = require("@ai-matrx/kit/sha256");
2747
+
2748
+ // models/device-models.ts
2749
+ var DEVICE_SERVED_VIA = "device";
2750
+ var DEVICE_FILE_USABLE_STATES = Object.freeze(["present"]);
2751
+ var DEVICE_OWN_FILE_USABLE_STATES = Object.freeze(["present", "unmatched"]);
2752
+ var s2 = (v) => typeof v === "string" ? v : "";
2753
+ var sn2 = (v) => typeof v === "string" ? v : null;
2754
+ var n = (v) => typeof v === "number" && Number.isFinite(v) ? v : typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : null;
2755
+ function toDeviceModelFile(r) {
2756
+ return {
2757
+ id: s2(r.id),
2758
+ app_instance_id: s2(r.app_instance_id),
2759
+ root_id: s2(r.root_id),
2760
+ rel_path: sn2(r.rel_path),
2761
+ size_bytes: n(r.size_bytes),
2762
+ sha256: sn2(r.sha256),
2763
+ artifact_id: sn2(r.artifact_id),
2764
+ state: s2(r.state),
2765
+ organization_id: sn2(r.organization_id)
2766
+ };
2767
+ }
2768
+ async function readDeviceModelFiles(client, opts = {}) {
2769
+ const db = client;
2770
+ let q = db.schema("ai").from("device_model_file").select("*").is("deleted_at", null).in("state", opts.states ?? DEVICE_FILE_USABLE_STATES);
2771
+ if (opts.appInstanceIds) q = q.in("app_instance_id", opts.appInstanceIds);
2772
+ const { data, error } = await q;
2773
+ if (error) throw new Error(`ai.device_model_file: ${error.message}`);
2774
+ return (data ?? []).filter((r) => r.deleted_at == null).map(toDeviceModelFile);
2775
+ }
2776
+ function requiredArtifactIds(v) {
2777
+ const core = v.artifacts.filter((a) => a.required_for.includes("core"));
2778
+ return (core.length ? core : v.artifacts).map((a) => a.id);
2779
+ }
2780
+ function availableOnDevices(variants, files) {
2781
+ const usable = files.filter((f) => f.artifact_id && DEVICE_FILE_USABLE_STATES.includes(f.state));
2782
+ const out = /* @__PURE__ */ new Map();
2783
+ for (const v of variants) {
2784
+ const order = new Map(v.artifacts.map((a, i) => [a.id, i]));
2785
+ const required = requiredArtifactIds(v);
2786
+ const byDevice = /* @__PURE__ */ new Map();
2787
+ for (const f of usable)
2788
+ if (order.has(f.artifact_id)) {
2789
+ const list2 = byDevice.get(f.app_instance_id) ?? [];
2790
+ list2.push(f);
2791
+ byDevice.set(f.app_instance_id, list2);
2792
+ }
2793
+ const list = [];
2794
+ for (const device of [...byDevice.keys()].sort()) {
2795
+ const held = byDevice.get(device);
2796
+ const have = new Set(held.map((f) => f.artifact_id));
2797
+ if (!required.length || !required.every((id) => have.has(id))) continue;
2798
+ list.push({
2799
+ app_instance_id: device,
2800
+ variant_id: v.id,
2801
+ device_model_file_ids: held.slice().sort((a, b) => order.get(a.artifact_id) - order.get(b.artifact_id) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).map((f) => f.id)
2802
+ });
2803
+ }
2804
+ out.set(v.id, list);
2805
+ }
2806
+ return out;
2807
+ }
2808
+ var DeviceModelUnavailableError = class extends Error {
2809
+ code;
2810
+ constructor(code, message) {
2811
+ super(message);
2812
+ this.name = "DeviceModelUnavailableError";
2813
+ this.code = code;
2814
+ }
2815
+ };
2816
+
2817
+ // models/catalog-reader.ts
2738
2818
  var MODEL_CATALOG_KINDS = /* @__PURE__ */ new Set([
2739
2819
  "llm_model",
2740
2820
  "whisper_model",
@@ -2772,14 +2852,17 @@ function catalogFingerprint(catalog) {
2772
2852
  const { catalog_fingerprint: _omit, ...rest } = catalog;
2773
2853
  return (0, import_sha256.sha256Hex)(canonicalJson(rest));
2774
2854
  }
2775
- var s2 = (v) => typeof v === "string" ? v : "";
2776
- var sn2 = (v) => typeof v === "string" ? v : null;
2777
- var n = (v) => typeof v === "number" && Number.isFinite(v) ? v : typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : null;
2855
+ var s3 = (v) => typeof v === "string" ? v : "";
2856
+ var sn3 = (v) => typeof v === "string" ? v : null;
2857
+ var n2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : null;
2778
2858
  var live2 = (r) => r.deleted_at == null;
2779
2859
  var byCodepoint2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
2780
- var byId = (rows3) => new Map(rows3.map((r) => [s2(r.id), r]));
2860
+ var byId = (rows3) => new Map(rows3.map((r) => [s3(r.id), r]));
2781
2861
  var strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
2782
2862
  async function readModelCatalog(client, opts = {}) {
2863
+ return readCatalogWith(client, opts, null);
2864
+ }
2865
+ async function readCatalogWith(client, opts, preread) {
2783
2866
  const db = client;
2784
2867
  const offeredOnly = opts.offeredOnly ?? true;
2785
2868
  const ai = db.schema("ai");
@@ -2797,20 +2880,29 @@ async function readModelCatalog(client, opts = {}) {
2797
2880
  ]);
2798
2881
  const curated = curatedList(shapeCuratedModels(raw), { offeredOnly });
2799
2882
  const listed = new Set(curated.map((m) => m.id));
2800
- const variants = raw.variants.filter((v) => listed.has(s2(v.model_id)) && (offeredOnly ? s2(v.review_state) === "offered" : isListedVariant(v)));
2883
+ const variants = raw.variants.filter((v) => listed.has(s3(v.model_id)) && (offeredOnly ? s3(v.review_state) === "offered" : isListedVariant(v)));
2801
2884
  const entryIds = [...new Set(curated.flatMap((m) => m.variants.flatMap((v) => v.catalog_entry_ids)))].sort(byCodepoint2);
2802
2885
  const warnings = [];
2803
- const [artifacts, entries] = await Promise.all([
2804
- variants.length ? all("model_artifact", (q) => q.in("variant_id", variants.map((v) => s2(v.id)))) : Promise.resolve([]),
2886
+ const devices = opts.devices ? opts.devices === true ? {} : opts.devices : null;
2887
+ const [artifacts, entries, deviceFiles] = await Promise.all([
2888
+ variants.length ? all("model_artifact", (q) => q.in("variant_id", variants.map((v) => s3(v.id)))) : Promise.resolve([]),
2805
2889
  entryIds.length ? Promise.resolve(db.schema("public").from("catalog_entries").select("id,kind").in("id", entryIds)).then(({ data, error }) => {
2806
2890
  if (error) {
2807
2891
  warnings.push(`public.catalog_entries: ${error.message} (models placed by what they do)`);
2808
2892
  return [];
2809
2893
  }
2810
2894
  return data ?? [];
2811
- }) : Promise.resolve([])
2895
+ }) : Promise.resolve([]),
2896
+ devices ? preread ? Promise.resolve(preread) : readDeviceModelFiles(client, devices.appInstanceIds ? { appInstanceIds: devices.appInstanceIds } : {}) : Promise.resolve(null)
2812
2897
  ]);
2813
- const kindOfEntry = new Map(entries.map((e) => [s2(e.id), s2(e.kind)]));
2898
+ const placement2 = deviceFiles ? availableOnDevices(
2899
+ variants.map((v) => ({
2900
+ id: s3(v.id),
2901
+ artifacts: artifacts.filter((a) => s3(a.variant_id) === s3(v.id)).map((a) => ({ id: s3(a.id), required_for: strings(a.required_for) }))
2902
+ })),
2903
+ deviceFiles
2904
+ ) : null;
2905
+ const kindOfEntry = new Map(entries.map((e) => [s3(e.id), s3(e.kind)]));
2814
2906
  const stamps = [];
2815
2907
  const stamp = (r) => {
2816
2908
  if (r && typeof r.updated_at === "string") stamps.push(r.updated_at);
@@ -2818,84 +2910,87 @@ async function readModelCatalog(client, opts = {}) {
2818
2910
  const modelById = byId(raw.models);
2819
2911
  const providerById = byId(raw.providers);
2820
2912
  const upstreamById = byId(upstreams);
2821
- const presentationOf = new Map(raw.presentations.map((p) => [s2(p.model_id), p]));
2913
+ const presentationOf = new Map(raw.presentations.map((p) => [s3(p.model_id), p]));
2822
2914
  const models = curated.flatMap((c) => {
2823
2915
  const m = modelById.get(c.id);
2824
2916
  if (!m) return [];
2825
2917
  stamp(m);
2826
2918
  const p = presentationOf.get(c.id);
2827
2919
  stamp(p);
2828
- const maker = providerById.get(s2(m.provider_id));
2920
+ const maker = providerById.get(s3(m.provider_id));
2829
2921
  return [
2830
2922
  {
2831
- id: s2(m.id),
2832
- name: s2(m.name),
2833
- desktop_id: sn2(m.metadata?.desktop_id),
2834
- common_name: sn2(m.common_name),
2835
- maker: sn2(maker?.name),
2836
- maker_logo_file_id: sn2(maker?.logo_file_id),
2837
- description: sn2(m.description),
2923
+ id: s3(m.id),
2924
+ name: s3(m.name),
2925
+ desktop_id: sn3(m.metadata?.desktop_id),
2926
+ common_name: sn3(m.common_name),
2927
+ maker: sn3(maker?.name),
2928
+ maker_logo_file_id: sn3(maker?.logo_file_id),
2929
+ description: sn3(m.description),
2838
2930
  capabilities: m.capabilities ?? {},
2839
2931
  kind: catalogKindOf(c, kindOfEntry),
2840
2932
  offered: c.offered,
2841
2933
  updated_at: c.updated_at,
2842
2934
  presentation: p ? {
2843
- summary: sn2(p.summary),
2844
- symbol: sn2(p.symbol),
2845
- color: sn2(p.color),
2846
- shelf: sn2(p.shelf),
2847
- shelf_title: sn2(p.shelf_title),
2848
- shelf_subtitle: sn2(p.shelf_subtitle),
2849
- display_order: n(p.display_order),
2850
- page: sn2(p.page),
2851
- params_label: sn2(p.params_label),
2935
+ summary: sn3(p.summary),
2936
+ symbol: sn3(p.symbol),
2937
+ color: sn3(p.color),
2938
+ shelf: sn3(p.shelf),
2939
+ shelf_title: sn3(p.shelf_title),
2940
+ shelf_subtitle: sn3(p.shelf_subtitle),
2941
+ display_order: n2(p.display_order),
2942
+ page: sn3(p.page),
2943
+ params_label: sn3(p.params_label),
2852
2944
  tags: strings(p.tags),
2853
2945
  links: Array.isArray(p.links) ? p.links : [],
2854
- thumbnail_file_id: sn2(p.thumbnail_file_id),
2855
- hero_file_id: sn2(p.hero_file_id),
2946
+ thumbnail_file_id: sn3(p.thumbnail_file_id),
2947
+ hero_file_id: sn3(p.hero_file_id),
2856
2948
  gallery_file_ids: strings(p.gallery_file_ids).filter((f) => f.length > 0),
2857
2949
  facts: p.facts && typeof p.facts === "object" && !Array.isArray(p.facts) ? p.facts : {}
2858
2950
  } : null,
2859
- variants: variants.filter((v) => s2(v.model_id) === c.id).sort((a, b) => byCodepoint2(s2(a.key), s2(b.key))).map((v) => {
2951
+ variants: variants.filter((v) => s3(v.model_id) === c.id).sort((a, b) => byCodepoint2(s3(a.key), s3(b.key))).map((v) => {
2860
2952
  stamp(v);
2861
- const up = upstreamById.get(s2(v.upstream_id));
2953
+ const up = upstreamById.get(s3(v.upstream_id));
2862
2954
  stamp(up);
2863
- const arts = artifacts.filter((a) => s2(a.variant_id) === s2(v.id)).sort((a, b) => (n(a.shard_index) ?? 0) - (n(b.shard_index) ?? 0) || byCodepoint2(s2(a.path_in_repo), s2(b.path_in_repo)));
2955
+ const arts = artifacts.filter((a) => s3(a.variant_id) === s3(v.id)).sort((a, b) => (n2(a.shard_index) ?? 0) - (n2(b.shard_index) ?? 0) || byCodepoint2(s3(a.path_in_repo), s3(b.path_in_repo)));
2864
2956
  arts.forEach(stamp);
2865
2957
  return {
2866
- id: s2(v.id),
2867
- key: s2(v.key),
2868
- catalog_key: `${s2(m.name)}/${s2(v.key)}`,
2869
- review_state: s2(v.review_state),
2958
+ id: s3(v.id),
2959
+ key: s3(v.key),
2960
+ catalog_key: `${s3(m.name)}/${s3(v.key)}`,
2961
+ review_state: s3(v.review_state),
2870
2962
  catalog_entry_ids: strings(v.metadata?.catalog_entry_ids),
2871
2963
  is_default: v.is_default === true,
2872
- format: s2(v.format),
2873
- quant: s2(v.quant),
2874
- architecture: s2(v.architecture),
2875
- chat_template_kind: s2(v.chat_template_kind),
2964
+ format: s3(v.format),
2965
+ quant: s3(v.quant),
2966
+ architecture: s3(v.architecture),
2967
+ chat_template_kind: s3(v.chat_template_kind),
2876
2968
  os_arch: strings(v.os_arch),
2877
- selection_rank: n(v.selection_rank) ?? Number.MAX_SAFE_INTEGER,
2969
+ selection_rank: n2(v.selection_rank) ?? Number.MAX_SAFE_INTEGER,
2878
2970
  // internal-bound: a row with no rank sorts last (the column is NOT NULL; this only orders a malformed row)
2879
- license: sn2(v.license_id),
2880
- gated: s2(v.gated),
2881
- total_bytes: n(v.total_bytes),
2882
- memory_bytes_min: n(v.memory_bytes_min),
2883
- memory_bytes_recommended: n(v.memory_bytes_recommended),
2971
+ license: sn3(v.license_id),
2972
+ gated: s3(v.gated),
2973
+ total_bytes: n2(v.total_bytes),
2974
+ memory_bytes_min: n2(v.memory_bytes_min),
2975
+ memory_bytes_recommended: n2(v.memory_bytes_recommended),
2884
2976
  tested_on_current_runtime: v.tested_on_current_runtime === true,
2885
- min_app_version: sn2(v.min_app_version),
2886
- upstream: up ? { kind: s2(up.upstream_kind), repo: sn2(up.repo), revision: sn2(up.revision) } : null,
2977
+ min_app_version: sn3(v.min_app_version),
2978
+ upstream: up ? { kind: s3(up.upstream_kind), repo: sn3(up.repo), revision: sn3(up.revision) } : null,
2887
2979
  artifacts: arts.map((a) => ({
2888
- role: s2(a.role),
2889
- path: s2(a.path_in_repo),
2890
- url: s2(a.url),
2891
- size_bytes: n(a.size_bytes) ?? 0,
2892
- sha256: s2(a.sha256),
2980
+ role: s3(a.role),
2981
+ path: s3(a.path_in_repo),
2982
+ url: s3(a.url),
2983
+ size_bytes: n2(a.size_bytes) ?? 0,
2984
+ sha256: s3(a.sha256),
2893
2985
  required_for: strings(a.required_for).sort(),
2894
- shard_index: n(a.shard_index),
2895
- shard_count: n(a.shard_count)
2986
+ shard_index: n2(a.shard_index),
2987
+ shard_count: n2(a.shard_count)
2896
2988
  }))
2897
2989
  };
2898
- })
2990
+ }),
2991
+ ...placement2 ? {
2992
+ available_on: variants.filter((v) => s3(v.model_id) === c.id).sort((a, b) => byCodepoint2(s3(a.key), s3(b.key))).flatMap((v) => placement2.get(s3(v.id)) ?? [])
2993
+ } : {}
2899
2994
  }
2900
2995
  ];
2901
2996
  });
@@ -2903,36 +2998,36 @@ async function readModelCatalog(client, opts = {}) {
2903
2998
  $schema: "matrx.model-catalog.snapshot/v1",
2904
2999
  catalog_updated_at: "",
2905
3000
  models,
2906
- runtimes: runtimes.sort((a, b) => byCodepoint2(s2(a.key), s2(b.key))).map((rt) => {
3001
+ runtimes: runtimes.sort((a, b) => byCodepoint2(s3(a.key), s3(b.key))).map((rt) => {
2907
3002
  stamp(rt);
2908
3003
  return {
2909
- key: s2(rt.key),
2910
- name: s2(rt.name),
2911
- upstream_repo: sn2(rt.upstream_repo),
2912
- builds: builds.filter((b) => s2(b.runtime_id) === s2(rt.id)).map((b) => {
3004
+ key: s3(rt.key),
3005
+ name: s3(rt.name),
3006
+ upstream_repo: sn3(rt.upstream_repo),
3007
+ builds: builds.filter((b) => s3(b.runtime_id) === s3(rt.id)).map((b) => {
2913
3008
  stamp(b);
2914
3009
  const known = b.metadata?.known_architectures;
2915
3010
  return {
2916
- id: s2(b.id),
2917
- version: s2(b.runtime_version),
2918
- os: s2(b.os),
2919
- arch: s2(b.arch),
2920
- accelerator: s2(b.accelerator),
2921
- build_kind: s2(b.build_kind),
2922
- url: sn2(b.url),
2923
- size_bytes: n(b.size_bytes),
2924
- sha256: sn2(b.sha256),
3011
+ id: s3(b.id),
3012
+ version: s3(b.runtime_version),
3013
+ os: s3(b.os),
3014
+ arch: s3(b.arch),
3015
+ accelerator: s3(b.accelerator),
3016
+ build_kind: s3(b.build_kind),
3017
+ url: sn3(b.url),
3018
+ size_bytes: n2(b.size_bytes),
3019
+ sha256: sn3(b.sha256),
2925
3020
  is_active: b.is_active === true,
2926
- min_app_version: sn2(b.min_app_version),
3021
+ min_app_version: sn3(b.min_app_version),
2927
3022
  // The architectures this pinned llama-server build loads (runtime_build.metadata): the own-model judge reads it.
2928
3023
  known_architectures: Array.isArray(known) ? known.map(String).sort() : null,
2929
- compat: compat.filter((c) => s2(c.runtime_build_id) === s2(b.id)).map((c) => ({
2930
- hardware_class: s2(c.hardware_class),
2931
- format: s2(c.format),
2932
- quant: s2(c.quant),
2933
- architecture: s2(c.architecture),
2934
- chat_template_kind: s2(c.chat_template_kind),
2935
- status: s2(c.status)
3024
+ compat: compat.filter((c) => s3(c.runtime_build_id) === s3(b.id)).map((c) => ({
3025
+ hardware_class: s3(c.hardware_class),
3026
+ format: s3(c.format),
3027
+ quant: s3(c.quant),
3028
+ architecture: s3(c.architecture),
3029
+ chat_template_kind: s3(c.chat_template_kind),
3030
+ status: s3(c.status)
2936
3031
  }))
2937
3032
  };
2938
3033
  })
@@ -2944,6 +3039,37 @@ async function readModelCatalog(client, opts = {}) {
2944
3039
  out.catalog_fingerprint = catalogFingerprint(out);
2945
3040
  return out;
2946
3041
  }
3042
+ async function resolveDeviceModel(client, req) {
3043
+ const files = await readDeviceModelFiles(client, {
3044
+ appInstanceIds: [req.app_instance_id],
3045
+ states: [.../* @__PURE__ */ new Set([...DEVICE_FILE_USABLE_STATES, ...DEVICE_OWN_FILE_USABLE_STATES])]
3046
+ });
3047
+ const own = files.find((f) => f.id === req.local_model_id);
3048
+ if (own) {
3049
+ if (own.artifact_id)
3050
+ throw new DeviceModelUnavailableError("not_own_file", `device model file ${own.id} is catalog artifact ${own.artifact_id}; pass its model id, not the file id`);
3051
+ if (!DEVICE_OWN_FILE_USABLE_STATES.includes(own.state))
3052
+ throw new DeviceModelUnavailableError("file_not_usable", `own model file ${own.id} on computer ${req.app_instance_id} is ${own.state}`);
3053
+ return { own: true, file: own };
3054
+ }
3055
+ const usable = files.filter((f) => DEVICE_FILE_USABLE_STATES.includes(f.state));
3056
+ const catalog = await readCatalogWith(client, { offeredOnly: false, devices: { appInstanceIds: [req.app_instance_id] } }, usable);
3057
+ const model = catalog.models.find((m) => m.id === req.local_model_id);
3058
+ if (!model)
3059
+ throw new DeviceModelUnavailableError("unknown_model", `no catalog model or own model file ${req.local_model_id} on computer ${req.app_instance_id}`);
3060
+ const label = model.common_name || model.name;
3061
+ if (req.variant_id && !model.variants.some((v) => v.id === req.variant_id))
3062
+ throw new DeviceModelUnavailableError("unknown_variant", `${label} has no listed variant ${req.variant_id}`);
3063
+ const held = (model.available_on ?? []).filter((a) => a.app_instance_id === req.app_instance_id && (!req.variant_id || a.variant_id === req.variant_id));
3064
+ const variants = held.map((a) => ({ a, v: model.variants.find((v) => v.id === a.variant_id) })).sort((x, y) => Number(y.v.is_default) - Number(x.v.is_default) || x.v.selection_rank - y.v.selection_rank || byCodepoint2(x.v.key, y.v.key));
3065
+ const pick = variants[0];
3066
+ if (!pick) {
3067
+ const which = req.variant_id ? ` (${model.variants.find((v) => v.id === req.variant_id).key})` : "";
3068
+ throw new DeviceModelUnavailableError("not_on_device", `computer ${req.app_instance_id} does not hold every required file of ${label}${which}`);
3069
+ }
3070
+ const byFileId = new Map(usable.map((f) => [f.id, f]));
3071
+ return { own: false, model, variant: pick.v, files: pick.a.device_model_file_ids.map((id) => byFileId.get(id)) };
3072
+ }
2947
3073
 
2948
3074
  // models/fit.ts
2949
3075
  var featuresOf = (c) => {