@ai-matrx/agents 0.59.268 → 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.
- package/CHANGELOG.md +6 -1
- package/dist/context/react/index.cjs +12 -1
- package/dist/context/react/index.js +12 -1
- package/dist/models/index.cjs +364 -238
- package/dist/models/index.d.cts +140 -14
- package/dist/models/index.d.ts +140 -14
- package/dist/models/index.js +364 -238
- package/dist/models/react/index.d.cts +9 -1
- package/dist/models/react/index.d.ts +9 -1
- package/package.json +2 -2
- package/scripts/check-model-catalog-reads.mjs +11 -6
package/dist/models/index.js
CHANGED
|
@@ -247,13 +247,13 @@ function setModelDiagnosticsSink(sink) {
|
|
|
247
247
|
}
|
|
248
248
|
function reportModelDiagnostic(dedupeKey, entry) {
|
|
249
249
|
try {
|
|
250
|
-
const
|
|
251
|
-
if (
|
|
252
|
-
|
|
250
|
+
const s4 = state();
|
|
251
|
+
if (s4.reported.has(dedupeKey)) return;
|
|
252
|
+
s4.reported.add(dedupeKey);
|
|
253
253
|
console.warn(`[parseCapabilities] ${entry.message}`);
|
|
254
|
-
if (
|
|
255
|
-
else if (!
|
|
256
|
-
|
|
254
|
+
if (s4.sink) s4.sink(entry);
|
|
255
|
+
else if (!s4.announced) {
|
|
256
|
+
s4.announced = true;
|
|
257
257
|
console.error(
|
|
258
258
|
"[@ai-matrx/agents/models] No diagnostics sink is bound \u2014 capability-vocabulary drift reaches the console only. Call setModelDiagnosticsSink(...) with your error store."
|
|
259
259
|
);
|
|
@@ -926,13 +926,13 @@ function createModelRecords(config) {
|
|
|
926
926
|
if (known.fetchScope === "active" || known.fetchScope === "all") {
|
|
927
927
|
jobs.push(
|
|
928
928
|
readOptions().then(
|
|
929
|
-
(rows3) => update((
|
|
930
|
-
const entities = { ...
|
|
929
|
+
(rows3) => update((s4) => {
|
|
930
|
+
const entities = { ...s4.entities };
|
|
931
931
|
for (const row of rows3) {
|
|
932
932
|
const existing = entities[row.id];
|
|
933
933
|
entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: existing?._fetchType === "full" ? "full" : "options" };
|
|
934
934
|
}
|
|
935
|
-
return withIdLists({ ...
|
|
935
|
+
return withIdLists({ ...s4, entities, lastFetched: Date.now(), error: null });
|
|
936
936
|
}),
|
|
937
937
|
(err) => fail("model-records-refresh-options-failed", messageOf(err))
|
|
938
938
|
)
|
|
@@ -942,9 +942,9 @@ function createModelRecords(config) {
|
|
|
942
942
|
if (record._fetchType !== "full") continue;
|
|
943
943
|
jobs.push(
|
|
944
944
|
readFullModel(id).then(
|
|
945
|
-
(fresh) => update((
|
|
946
|
-
const existing =
|
|
947
|
-
return withIdLists({ ...
|
|
945
|
+
(fresh) => update((s4) => {
|
|
946
|
+
const existing = s4.entities[fresh.id];
|
|
947
|
+
return withIdLists({ ...s4, entities: { ...s4.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
|
|
948
948
|
}),
|
|
949
949
|
(err) => fail("model-records-refresh-model-failed", `${id}: ${messageOf(err)}`, { modelId: id })
|
|
950
950
|
)
|
|
@@ -955,7 +955,7 @@ function createModelRecords(config) {
|
|
|
955
955
|
Promise.resolve(ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(id), id).maybeSingle()).then(
|
|
956
956
|
({ data, error }) => {
|
|
957
957
|
if (error) throw error;
|
|
958
|
-
if (data) update((
|
|
958
|
+
if (data) update((s4) => ({ ...s4, identityById: { ...s4.identityById, [id]: data } }));
|
|
959
959
|
}
|
|
960
960
|
).catch((err) => fail("model-records-refresh-identity-failed", `${id}: ${messageOf(err)}`, { modelId: id }))
|
|
961
961
|
);
|
|
@@ -963,7 +963,7 @@ function createModelRecords(config) {
|
|
|
963
963
|
if (known.modelClassStatus === "succeeded") {
|
|
964
964
|
jobs.push(
|
|
965
965
|
readOfferings().then(
|
|
966
|
-
(rows3) => update((
|
|
966
|
+
(rows3) => update((s4) => ({ ...s4, modelClassIndex: buildModelClassIndex(rows3) })),
|
|
967
967
|
(err) => fail("model-records-refresh-classes-failed", messageOf(err))
|
|
968
968
|
)
|
|
969
969
|
);
|
|
@@ -971,11 +971,11 @@ function createModelRecords(config) {
|
|
|
971
971
|
for (const id of classesReadFor) {
|
|
972
972
|
jobs.push(
|
|
973
973
|
readOfferings(id).then(
|
|
974
|
-
(rows3) => update((
|
|
975
|
-
if (
|
|
974
|
+
(rows3) => update((s4) => {
|
|
975
|
+
if (s4.modelClassStatus === "succeeded") return s4;
|
|
976
976
|
const slice = buildModelClassIndex(rows3);
|
|
977
|
-
const base =
|
|
978
|
-
return { ...
|
|
977
|
+
const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
|
|
978
|
+
return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
|
|
979
979
|
}),
|
|
980
980
|
(err) => fail("model-records-refresh-classes-failed", `${id}: ${messageOf(err)}`, { modelId: id })
|
|
981
981
|
)
|
|
@@ -989,7 +989,7 @@ function createModelRecords(config) {
|
|
|
989
989
|
({ data, error }) => {
|
|
990
990
|
if (error) throw error;
|
|
991
991
|
const next = data && typeof data === "object" && !Array.isArray(data) ? data : null;
|
|
992
|
-
update((
|
|
992
|
+
update((s4) => ({ ...s4, classConfigByOffering: { ...s4.classConfigByOffering, [key]: { modelId: cfg.modelId, controls: next?.controls ?? null, constraints: next?.constraints ?? null } } }));
|
|
993
993
|
}
|
|
994
994
|
).catch((err) => fail("model-records-refresh-class-config-failed", `${key}: ${messageOf(err)}`))
|
|
995
995
|
);
|
|
@@ -1035,29 +1035,29 @@ function createModelRecords(config) {
|
|
|
1035
1035
|
loadOptions() {
|
|
1036
1036
|
if (state2.fetchScope === "active" || state2.fetchScope === "all") return Promise.resolve();
|
|
1037
1037
|
return once("options", async () => {
|
|
1038
|
-
update((
|
|
1038
|
+
update((s4) => ({ ...s4, isLoading: true, error: null }));
|
|
1039
1039
|
try {
|
|
1040
1040
|
const rows3 = await readOptions();
|
|
1041
|
-
update((
|
|
1042
|
-
const entities = { ...
|
|
1041
|
+
update((s4) => {
|
|
1042
|
+
const entities = { ...s4.entities };
|
|
1043
1043
|
for (const row of rows3) {
|
|
1044
1044
|
const existing = entities[row.id];
|
|
1045
1045
|
if (existing?._fetchType === "full") continue;
|
|
1046
1046
|
entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: "options" };
|
|
1047
1047
|
}
|
|
1048
1048
|
return withIdLists({
|
|
1049
|
-
...
|
|
1049
|
+
...s4,
|
|
1050
1050
|
entities,
|
|
1051
1051
|
isLoading: false,
|
|
1052
1052
|
error: null,
|
|
1053
|
-
fetchScope:
|
|
1053
|
+
fetchScope: s4.fetchScope !== "all" && s4.fetchScope !== "deprecated" ? "active" : s4.fetchScope,
|
|
1054
1054
|
lastFetched: Date.now()
|
|
1055
1055
|
});
|
|
1056
1056
|
});
|
|
1057
1057
|
} catch (err) {
|
|
1058
1058
|
const message = messageOf(err);
|
|
1059
1059
|
errorSink({ code: "model-records-options-load-failed", message });
|
|
1060
|
-
update((
|
|
1060
|
+
update((s4) => ({ ...s4, isLoading: false, error: message }));
|
|
1061
1061
|
}
|
|
1062
1062
|
});
|
|
1063
1063
|
},
|
|
@@ -1067,23 +1067,23 @@ function createModelRecords(config) {
|
|
|
1067
1067
|
return inFlight.get(`model:${modelId}`) ?? Promise.resolve();
|
|
1068
1068
|
}
|
|
1069
1069
|
return once(`model:${modelId}`, async () => {
|
|
1070
|
-
update((
|
|
1071
|
-
const detailErrorById = { ...
|
|
1070
|
+
update((s4) => {
|
|
1071
|
+
const detailErrorById = { ...s4.detailErrorById };
|
|
1072
1072
|
delete detailErrorById[modelId];
|
|
1073
|
-
return { ...
|
|
1073
|
+
return { ...s4, detailStatusById: { ...s4.detailStatusById, [modelId]: "loading" }, detailErrorById };
|
|
1074
1074
|
});
|
|
1075
1075
|
try {
|
|
1076
1076
|
const record = await readFullModel(modelId);
|
|
1077
|
-
update((
|
|
1078
|
-
const existing =
|
|
1079
|
-
const detailErrorById = { ...
|
|
1077
|
+
update((s4) => {
|
|
1078
|
+
const existing = s4.entities[record.id];
|
|
1079
|
+
const detailErrorById = { ...s4.detailErrorById };
|
|
1080
1080
|
delete detailErrorById[modelId];
|
|
1081
1081
|
return withIdLists({
|
|
1082
|
-
...
|
|
1083
|
-
detailStatusById: { ...
|
|
1082
|
+
...s4,
|
|
1083
|
+
detailStatusById: { ...s4.detailStatusById, [modelId]: "succeeded" },
|
|
1084
1084
|
detailErrorById,
|
|
1085
1085
|
entities: {
|
|
1086
|
-
...
|
|
1086
|
+
...s4.entities,
|
|
1087
1087
|
[record.id]: { ...emptyRecord(), ...existing, ...record, maker: record.maker ?? existing?.maker ?? null, _fetchType: "full" }
|
|
1088
1088
|
}
|
|
1089
1089
|
});
|
|
@@ -1091,21 +1091,21 @@ function createModelRecords(config) {
|
|
|
1091
1091
|
} catch (err) {
|
|
1092
1092
|
const message = `Model configuration ${modelId}: ${messageOf(err)}`;
|
|
1093
1093
|
errorSink({ code: "model-records-model-load-failed", message, context: { modelId } });
|
|
1094
|
-
update((
|
|
1095
|
-
...
|
|
1096
|
-
detailStatusById: { ...
|
|
1097
|
-
detailErrorById: { ...
|
|
1094
|
+
update((s4) => ({
|
|
1095
|
+
...s4,
|
|
1096
|
+
detailStatusById: { ...s4.detailStatusById, [modelId]: "failed" },
|
|
1097
|
+
detailErrorById: { ...s4.detailErrorById, [modelId]: message }
|
|
1098
1098
|
}));
|
|
1099
1099
|
}
|
|
1100
1100
|
});
|
|
1101
1101
|
},
|
|
1102
1102
|
retryModel(modelId) {
|
|
1103
|
-
update((
|
|
1104
|
-
const detailStatusById = { ...
|
|
1105
|
-
const detailErrorById = { ...
|
|
1103
|
+
update((s4) => {
|
|
1104
|
+
const detailStatusById = { ...s4.detailStatusById };
|
|
1105
|
+
const detailErrorById = { ...s4.detailErrorById };
|
|
1106
1106
|
delete detailStatusById[modelId];
|
|
1107
1107
|
delete detailErrorById[modelId];
|
|
1108
|
-
return { ...
|
|
1108
|
+
return { ...s4, detailStatusById, detailErrorById };
|
|
1109
1109
|
});
|
|
1110
1110
|
},
|
|
1111
1111
|
loadClassConfig(modelId, offeringId) {
|
|
@@ -1113,37 +1113,37 @@ function createModelRecords(config) {
|
|
|
1113
1113
|
const status = state2.classConfigStatusByOffering[key];
|
|
1114
1114
|
if (status === "loading" || status === "succeeded") return inFlight.get(`class:${key}`) ?? Promise.resolve();
|
|
1115
1115
|
return once(`class:${key}`, async () => {
|
|
1116
|
-
update((
|
|
1116
|
+
update((s4) => ({ ...s4, classConfigStatusByOffering: { ...s4.classConfigStatusByOffering, [key]: "loading" } }));
|
|
1117
1117
|
try {
|
|
1118
1118
|
const { data, error } = await ai().rpc("resolve_model_config", { p_model_id: modelId, p_offering_id: offeringId });
|
|
1119
1119
|
if (error) throw error;
|
|
1120
1120
|
const cfg = data && typeof data === "object" && !Array.isArray(data) ? data : null;
|
|
1121
|
-
update((
|
|
1122
|
-
...
|
|
1123
|
-
classConfigStatusByOffering: { ...
|
|
1121
|
+
update((s4) => ({
|
|
1122
|
+
...s4,
|
|
1123
|
+
classConfigStatusByOffering: { ...s4.classConfigStatusByOffering, [key]: "succeeded" },
|
|
1124
1124
|
classConfigByOffering: {
|
|
1125
|
-
...
|
|
1125
|
+
...s4.classConfigByOffering,
|
|
1126
1126
|
[key]: { modelId, controls: cfg?.controls ?? null, constraints: cfg?.constraints ?? null }
|
|
1127
1127
|
}
|
|
1128
1128
|
}));
|
|
1129
1129
|
} catch (err) {
|
|
1130
1130
|
errorSink({ code: "model-records-class-config-failed", message: `Model class configuration ${modelId} / ${offeringId}: ${messageOf(err)}` });
|
|
1131
|
-
update((
|
|
1131
|
+
update((s4) => ({ ...s4, classConfigStatusByOffering: { ...s4.classConfigStatusByOffering, [key]: "failed" } }));
|
|
1132
1132
|
}
|
|
1133
1133
|
});
|
|
1134
1134
|
},
|
|
1135
1135
|
loadClasses() {
|
|
1136
1136
|
if (state2.modelClassStatus === "loading" || state2.modelClassStatus === "succeeded") return inFlight.get("classes") ?? Promise.resolve();
|
|
1137
1137
|
return once("classes", async () => {
|
|
1138
|
-
update((
|
|
1138
|
+
update((s4) => ({ ...s4, modelClassStatus: "loading" }));
|
|
1139
1139
|
try {
|
|
1140
1140
|
const { data, error } = await ai().from("model_offering").select("model_id, offering_id, served_via, served_via_endpoint_id, priority");
|
|
1141
1141
|
if (error) throw error;
|
|
1142
1142
|
const index = buildModelClassIndex(data ?? []);
|
|
1143
|
-
update((
|
|
1143
|
+
update((s4) => ({ ...s4, modelClassStatus: "succeeded", modelClassIndex: index }));
|
|
1144
1144
|
} catch (err) {
|
|
1145
1145
|
errorSink({ code: "model-records-classes-failed", message: `Model classes: ${messageOf(err)}` });
|
|
1146
|
-
update((
|
|
1146
|
+
update((s4) => ({ ...s4, modelClassStatus: "failed" }));
|
|
1147
1147
|
}
|
|
1148
1148
|
});
|
|
1149
1149
|
},
|
|
@@ -1158,11 +1158,11 @@ function createModelRecords(config) {
|
|
|
1158
1158
|
if (error) throw error;
|
|
1159
1159
|
const slice = buildModelClassIndex(data ?? []);
|
|
1160
1160
|
classesReadFor.add(modelId);
|
|
1161
|
-
update((
|
|
1162
|
-
if (
|
|
1163
|
-
const base =
|
|
1161
|
+
update((s4) => {
|
|
1162
|
+
if (s4.modelClassStatus === "succeeded") return s4;
|
|
1163
|
+
const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
|
|
1164
1164
|
return {
|
|
1165
|
-
...
|
|
1165
|
+
...s4,
|
|
1166
1166
|
modelClassIndex: {
|
|
1167
1167
|
classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId },
|
|
1168
1168
|
classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering }
|
|
@@ -1180,20 +1180,20 @@ function createModelRecords(config) {
|
|
|
1180
1180
|
return inFlight.get(`identity:${modelId}`) ?? Promise.resolve();
|
|
1181
1181
|
}
|
|
1182
1182
|
return once(`identity:${modelId}`, async () => {
|
|
1183
|
-
update((
|
|
1183
|
+
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "loading" } }));
|
|
1184
1184
|
try {
|
|
1185
1185
|
const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(modelId), modelId).maybeSingle();
|
|
1186
1186
|
if (error) throw error;
|
|
1187
1187
|
if (!data) throw new Error(`AI model ${modelId} is not available (ai.model_definition has no such row)`);
|
|
1188
1188
|
const row = data;
|
|
1189
|
-
update((
|
|
1190
|
-
...
|
|
1191
|
-
identityById: { ...
|
|
1192
|
-
identityStatusById: { ...
|
|
1189
|
+
update((s4) => ({
|
|
1190
|
+
...s4,
|
|
1191
|
+
identityById: { ...s4.identityById, [row.id]: row, [modelId]: row },
|
|
1192
|
+
identityStatusById: { ...s4.identityStatusById, [row.id]: "succeeded", [modelId]: "succeeded" }
|
|
1193
1193
|
}));
|
|
1194
1194
|
} catch (err) {
|
|
1195
1195
|
errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelId } });
|
|
1196
|
-
update((
|
|
1196
|
+
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "failed" } }));
|
|
1197
1197
|
}
|
|
1198
1198
|
});
|
|
1199
1199
|
},
|
|
@@ -1212,26 +1212,26 @@ function createModelRecords(config) {
|
|
|
1212
1212
|
}
|
|
1213
1213
|
if (wanted.length > 0) {
|
|
1214
1214
|
const marked = Object.fromEntries(wanted.map((id) => [id, "loading"]));
|
|
1215
|
-
update((
|
|
1215
|
+
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...marked } }));
|
|
1216
1216
|
const run = (async () => {
|
|
1217
1217
|
try {
|
|
1218
1218
|
const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").in("id", wanted);
|
|
1219
1219
|
if (error) throw error;
|
|
1220
1220
|
const rows3 = Array.isArray(data) ? data : [];
|
|
1221
1221
|
const got = new Set(rows3.map((r) => r.id));
|
|
1222
|
-
update((
|
|
1223
|
-
...
|
|
1224
|
-
identityById: { ...
|
|
1222
|
+
update((s4) => ({
|
|
1223
|
+
...s4,
|
|
1224
|
+
identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
|
|
1225
1225
|
identityStatusById: {
|
|
1226
|
-
...
|
|
1226
|
+
...s4.identityStatusById,
|
|
1227
1227
|
...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"]))
|
|
1228
1228
|
}
|
|
1229
1229
|
}));
|
|
1230
1230
|
} catch (err) {
|
|
1231
1231
|
errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
|
|
1232
|
-
update((
|
|
1233
|
-
...
|
|
1234
|
-
identityStatusById: { ...
|
|
1232
|
+
update((s4) => ({
|
|
1233
|
+
...s4,
|
|
1234
|
+
identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) }
|
|
1235
1235
|
}));
|
|
1236
1236
|
}
|
|
1237
1237
|
})();
|
|
@@ -1249,11 +1249,11 @@ function createModelRecords(config) {
|
|
|
1249
1249
|
if (!classesReadFor.has(modelId) && state2.modelClassStatus !== "succeeded") {
|
|
1250
1250
|
const slice = buildModelClassIndex(offerings.filter((o) => o.model_id === modelId));
|
|
1251
1251
|
classesReadFor.add(modelId);
|
|
1252
|
-
update((
|
|
1253
|
-
if (
|
|
1254
|
-
const base =
|
|
1252
|
+
update((s4) => {
|
|
1253
|
+
if (s4.modelClassStatus === "succeeded") return s4;
|
|
1254
|
+
const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
|
|
1255
1255
|
return {
|
|
1256
|
-
...
|
|
1256
|
+
...s4,
|
|
1257
1257
|
modelClassIndex: {
|
|
1258
1258
|
classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId },
|
|
1259
1259
|
classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering }
|
|
@@ -1272,16 +1272,16 @@ function createModelRecords(config) {
|
|
|
1272
1272
|
controls: row.controls ?? null,
|
|
1273
1273
|
capabilities: requireCanonicalCapabilities(row.capabilities, { modelId: row.id, modelName: row.name })
|
|
1274
1274
|
});
|
|
1275
|
-
update((
|
|
1276
|
-
const existing =
|
|
1277
|
-
const detailErrorById = { ...
|
|
1275
|
+
update((s4) => {
|
|
1276
|
+
const existing = s4.entities[record.id];
|
|
1277
|
+
const detailErrorById = { ...s4.detailErrorById };
|
|
1278
1278
|
delete detailErrorById[modelId];
|
|
1279
1279
|
return withIdLists({
|
|
1280
|
-
...
|
|
1281
|
-
detailStatusById: { ...
|
|
1280
|
+
...s4,
|
|
1281
|
+
detailStatusById: { ...s4.detailStatusById, [modelId]: "succeeded" },
|
|
1282
1282
|
detailErrorById,
|
|
1283
1283
|
entities: {
|
|
1284
|
-
...
|
|
1284
|
+
...s4.entities,
|
|
1285
1285
|
[record.id]: { ...emptyRecord(), ...existing, ...record, maker: record.maker ?? existing?.maker ?? null, _fetchType: "full" }
|
|
1286
1286
|
}
|
|
1287
1287
|
});
|
|
@@ -1291,14 +1291,14 @@ function createModelRecords(config) {
|
|
|
1291
1291
|
}
|
|
1292
1292
|
},
|
|
1293
1293
|
hydrate({ models, fetchType, fetchScope, lastFetched }) {
|
|
1294
|
-
update((
|
|
1295
|
-
const entities = { ...
|
|
1294
|
+
update((s4) => {
|
|
1295
|
+
const entities = { ...s4.entities };
|
|
1296
1296
|
for (const raw of models) {
|
|
1297
1297
|
const existing = entities[raw.id];
|
|
1298
1298
|
if (existing?._fetchType === "full" && fetchType === "options") continue;
|
|
1299
1299
|
entities[raw.id] = { ...emptyRecord(), ...normalizeModel({ ...raw, controls: raw.controls ?? null }), _fetchType: fetchType };
|
|
1300
1300
|
}
|
|
1301
|
-
return withIdLists({ ...
|
|
1301
|
+
return withIdLists({ ...s4, entities, fetchScope, lastFetched, isLoading: false, error: null });
|
|
1302
1302
|
});
|
|
1303
1303
|
}
|
|
1304
1304
|
};
|
|
@@ -1318,28 +1318,28 @@ function memo(fn) {
|
|
|
1318
1318
|
var UNSET = /* @__PURE__ */ Symbol("unset");
|
|
1319
1319
|
var activeKey = null;
|
|
1320
1320
|
var activeOut = [];
|
|
1321
|
-
function selectActiveModels(
|
|
1322
|
-
if (!activeKey || activeKey[0] !==
|
|
1323
|
-
activeKey = [
|
|
1324
|
-
activeOut =
|
|
1321
|
+
function selectActiveModels(s4) {
|
|
1322
|
+
if (!activeKey || activeKey[0] !== s4.entities || activeKey[1] !== s4.activeIds) {
|
|
1323
|
+
activeKey = [s4.entities, s4.activeIds];
|
|
1324
|
+
activeOut = s4.activeIds.map((id) => s4.entities[id]).filter((m) => !!m);
|
|
1325
1325
|
}
|
|
1326
1326
|
return activeOut;
|
|
1327
1327
|
}
|
|
1328
1328
|
var deprecatedKey = null;
|
|
1329
1329
|
var deprecatedOut = [];
|
|
1330
|
-
function selectDeprecatedModels(
|
|
1331
|
-
if (!deprecatedKey || deprecatedKey[0] !==
|
|
1332
|
-
deprecatedKey = [
|
|
1333
|
-
deprecatedOut =
|
|
1330
|
+
function selectDeprecatedModels(s4) {
|
|
1331
|
+
if (!deprecatedKey || deprecatedKey[0] !== s4.entities || deprecatedKey[1] !== s4.deprecatedIds) {
|
|
1332
|
+
deprecatedKey = [s4.entities, s4.deprecatedIds];
|
|
1333
|
+
deprecatedOut = s4.deprecatedIds.map((id) => s4.entities[id]).filter((m) => !!m);
|
|
1334
1334
|
}
|
|
1335
1335
|
return deprecatedOut;
|
|
1336
1336
|
}
|
|
1337
1337
|
var allOf = memo((entities) => Object.values(entities));
|
|
1338
|
-
var selectAllModels = (
|
|
1338
|
+
var selectAllModels = (s4) => allOf(s4.entities);
|
|
1339
1339
|
var optionsOf = memo(
|
|
1340
1340
|
(models) => models.map((m) => ({ value: m.id, label: modelDisplayLabel(m) || m.id, maker: m.maker, costRating: m.cost_rating, speedRating: m.speed_rating }))
|
|
1341
1341
|
);
|
|
1342
|
-
var selectModelOptions = (
|
|
1342
|
+
var selectModelOptions = (s4) => optionsOf(selectActiveModels(s4));
|
|
1343
1343
|
var allOptionsOf = memo(
|
|
1344
1344
|
(entities) => Object.values(entities).map((m) => ({
|
|
1345
1345
|
value: m.id,
|
|
@@ -1350,30 +1350,30 @@ var allOptionsOf = memo(
|
|
|
1350
1350
|
successorId: m.successor_id ?? null
|
|
1351
1351
|
}))
|
|
1352
1352
|
);
|
|
1353
|
-
var selectAllModelOptions = (
|
|
1354
|
-
var selectModelById = (
|
|
1355
|
-
function selectModelLabelById(
|
|
1353
|
+
var selectAllModelOptions = (s4) => allOptionsOf(s4.entities);
|
|
1354
|
+
var selectModelById = (s4, modelId) => modelId ? s4.entities[modelId] : void 0;
|
|
1355
|
+
function selectModelLabelById(s4, modelId) {
|
|
1356
1356
|
if (!modelId) return void 0;
|
|
1357
|
-
const m =
|
|
1358
|
-
const identity =
|
|
1357
|
+
const m = s4.entities[modelId];
|
|
1358
|
+
const identity = s4.identityById[modelId];
|
|
1359
1359
|
return modelDisplayLabel(m) || modelDisplayLabel(identity);
|
|
1360
1360
|
}
|
|
1361
|
-
var selectModelNameById = (
|
|
1362
|
-
var selectModelClassName = (
|
|
1363
|
-
function selectModelLabelWithClass(
|
|
1364
|
-
const label = selectModelLabelById(
|
|
1365
|
-
return label ? withModelClass(label, selectModelClassName(
|
|
1366
|
-
}
|
|
1367
|
-
var selectModelControls = (
|
|
1368
|
-
var selectModelFetchType = (
|
|
1369
|
-
var selectModelFullyLoaded = (
|
|
1370
|
-
var selectModelDetailError = (
|
|
1371
|
-
var selectModelClassConfig = (
|
|
1372
|
-
var selectActiveModelsReady = (
|
|
1373
|
-
var selectDeprecatedModelsReady = (
|
|
1374
|
-
var selectAllModelsReady = (
|
|
1375
|
-
var selectModelIdentityById = (
|
|
1376
|
-
var selectModelIdentityLookupStatus = (
|
|
1361
|
+
var selectModelNameById = (s4, modelId) => modelId ? s4.entities[modelId]?.name : void 0;
|
|
1362
|
+
var selectModelClassName = (s4, modelId, offeringId) => modelClassName(s4.modelClassIndex, modelId, offeringId);
|
|
1363
|
+
function selectModelLabelWithClass(s4, modelId, offeringId) {
|
|
1364
|
+
const label = selectModelLabelById(s4, modelId);
|
|
1365
|
+
return label ? withModelClass(label, selectModelClassName(s4, modelId, offeringId)) : void 0;
|
|
1366
|
+
}
|
|
1367
|
+
var selectModelControls = (s4, modelId) => s4.entities[modelId]?.controls;
|
|
1368
|
+
var selectModelFetchType = (s4, modelId) => s4.entities[modelId]?._fetchType;
|
|
1369
|
+
var selectModelFullyLoaded = (s4, modelId) => !!modelId && s4.entities[modelId]?._fetchType === "full";
|
|
1370
|
+
var selectModelDetailError = (s4, modelId) => modelId && s4.detailErrorById[modelId] || null;
|
|
1371
|
+
var selectModelClassConfig = (s4, modelId, offeringId) => modelId && offeringId ? s4.classConfigByOffering[classConfigKey(modelId, offeringId)] : void 0;
|
|
1372
|
+
var selectActiveModelsReady = (s4) => (s4.fetchScope === "active" || s4.fetchScope === "all") && s4.activeIds.length > 0;
|
|
1373
|
+
var selectDeprecatedModelsReady = (s4) => (s4.fetchScope === "deprecated" || s4.fetchScope === "all") && s4.deprecatedIds.length > 0;
|
|
1374
|
+
var selectAllModelsReady = (s4) => s4.fetchScope === "all";
|
|
1375
|
+
var selectModelIdentityById = (s4, modelId) => s4.identityById[modelId];
|
|
1376
|
+
var selectModelIdentityLookupStatus = (s4, modelId) => s4.identityStatusById[modelId] ?? "idle";
|
|
1377
1377
|
async function readModelIdentities(client, modelIds) {
|
|
1378
1378
|
const ids = [...new Set(modelIds.filter(Boolean))];
|
|
1379
1379
|
if (ids.length === 0) return [];
|
|
@@ -1673,9 +1673,9 @@ function createModelCatalog(config) {
|
|
|
1673
1673
|
};
|
|
1674
1674
|
},
|
|
1675
1675
|
load(variant) {
|
|
1676
|
-
const
|
|
1677
|
-
if (
|
|
1678
|
-
if (
|
|
1676
|
+
const s4 = state2[variant];
|
|
1677
|
+
if (s4.status === "idle" || s4.status === "error") return run(variant);
|
|
1678
|
+
if (s4.status === "ready" && s4.loadedAt !== null && Date.now() - s4.loadedAt > staleAfterMs) return run(variant);
|
|
1679
1679
|
return inFlight[variant] ?? Promise.resolve();
|
|
1680
1680
|
},
|
|
1681
1681
|
reload: (variant) => run(variant)
|
|
@@ -1863,9 +1863,9 @@ function placement(v, runtimes, device) {
|
|
|
1863
1863
|
}
|
|
1864
1864
|
return "not_compatible";
|
|
1865
1865
|
}
|
|
1866
|
-
function formatMemoryBytes(
|
|
1867
|
-
const g =
|
|
1868
|
-
if (g < 1) return `${Math.max(1, Math.round(
|
|
1866
|
+
function formatMemoryBytes(n3) {
|
|
1867
|
+
const g = n3 / 2 ** 30;
|
|
1868
|
+
if (g < 1) return `${Math.max(1, Math.round(n3 / 2 ** 20))} MB`;
|
|
1869
1869
|
return `${g < 10 ? Math.round(g * 10) / 10 : Math.round(g)} GB`;
|
|
1870
1870
|
}
|
|
1871
1871
|
function describe(c, decided, runnerUp, device) {
|
|
@@ -2192,41 +2192,41 @@ var Cursor = class {
|
|
|
2192
2192
|
this.read = read;
|
|
2193
2193
|
this.size = size;
|
|
2194
2194
|
}
|
|
2195
|
-
async need(
|
|
2196
|
-
if (this.pos +
|
|
2197
|
-
if (this.pos +
|
|
2198
|
-
if (this.pos >= this.base && this.pos +
|
|
2199
|
-
const len = Math.min(Math.max(
|
|
2195
|
+
async need(n3) {
|
|
2196
|
+
if (this.pos + n3 > this.size) throw new GgufError("the file ends inside its header");
|
|
2197
|
+
if (this.pos + n3 > MAX_HEADER) throw new GgufError("its header is larger than any real model's");
|
|
2198
|
+
if (this.pos >= this.base && this.pos + n3 <= this.base + this.buf.length) return;
|
|
2199
|
+
const len = Math.min(Math.max(n3, CHUNK), this.size - this.pos);
|
|
2200
2200
|
this.buf = await this.read(this.pos, len);
|
|
2201
2201
|
this.base = this.pos;
|
|
2202
|
-
if (this.buf.length <
|
|
2202
|
+
if (this.buf.length < n3) throw new GgufError("the file ends inside its header");
|
|
2203
2203
|
}
|
|
2204
|
-
async view(
|
|
2205
|
-
await this.need(
|
|
2206
|
-
const v = new DataView(this.buf.buffer, this.buf.byteOffset + (this.pos - this.base),
|
|
2207
|
-
this.pos +=
|
|
2204
|
+
async view(n3) {
|
|
2205
|
+
await this.need(n3);
|
|
2206
|
+
const v = new DataView(this.buf.buffer, this.buf.byteOffset + (this.pos - this.base), n3);
|
|
2207
|
+
this.pos += n3;
|
|
2208
2208
|
return v;
|
|
2209
2209
|
}
|
|
2210
2210
|
async u32() {
|
|
2211
2211
|
return (await this.view(4)).getUint32(0, true);
|
|
2212
2212
|
}
|
|
2213
2213
|
async u64() {
|
|
2214
|
-
const
|
|
2215
|
-
if (
|
|
2216
|
-
return Number(
|
|
2214
|
+
const n3 = (await this.view(8)).getBigUint64(0, true);
|
|
2215
|
+
if (n3 > BigInt(Number.MAX_SAFE_INTEGER)) throw new GgufError("a size in its header is out of range");
|
|
2216
|
+
return Number(n3);
|
|
2217
2217
|
}
|
|
2218
2218
|
async str() {
|
|
2219
2219
|
const len = await this.u64();
|
|
2220
2220
|
if (len > MAX_STRING) throw new GgufError("a text value in its header is out of range");
|
|
2221
2221
|
await this.need(len);
|
|
2222
2222
|
const at = this.pos - this.base;
|
|
2223
|
-
const
|
|
2223
|
+
const s4 = new TextDecoder().decode(this.buf.subarray(at, at + len));
|
|
2224
2224
|
this.pos += len;
|
|
2225
|
-
return
|
|
2225
|
+
return s4;
|
|
2226
2226
|
}
|
|
2227
|
-
async skip(
|
|
2228
|
-
if (this.pos +
|
|
2229
|
-
this.pos +=
|
|
2227
|
+
async skip(n3) {
|
|
2228
|
+
if (this.pos + n3 > this.size) throw new GgufError("the file ends inside its header");
|
|
2229
|
+
this.pos += n3;
|
|
2230
2230
|
}
|
|
2231
2231
|
async scalar(type) {
|
|
2232
2232
|
switch (type) {
|
|
@@ -2278,11 +2278,11 @@ async function readGgufHeader(read, size) {
|
|
|
2278
2278
|
const type = await c.u32();
|
|
2279
2279
|
if (type === 9) {
|
|
2280
2280
|
const elem = await c.u32();
|
|
2281
|
-
const
|
|
2282
|
-
if (
|
|
2283
|
-
arrays[key] =
|
|
2284
|
-
if (elem === 8) for (let j = 0; j <
|
|
2285
|
-
else if (SCALAR_BYTES[elem] !== void 0) await c.skip(
|
|
2281
|
+
const n3 = await c.u64();
|
|
2282
|
+
if (n3 > 64 * MAX_COUNT) throw new GgufError("an array in its header is out of range");
|
|
2283
|
+
arrays[key] = n3;
|
|
2284
|
+
if (elem === 8) for (let j = 0; j < n3; j++) await c.str();
|
|
2285
|
+
else if (SCALAR_BYTES[elem] !== void 0) await c.skip(n3 * SCALAR_BYTES[elem]);
|
|
2286
2286
|
else throw new GgufError(`its header has an array of unknown type ${elem}`);
|
|
2287
2287
|
} else kv[key] = await c.scalar(type);
|
|
2288
2288
|
}
|
|
@@ -2312,20 +2312,20 @@ async function readGgufHeader(read, size) {
|
|
|
2312
2312
|
return { version, kv, array_lengths: arrays, tensors, data_offset: dataOffset, data_end: end };
|
|
2313
2313
|
}
|
|
2314
2314
|
function ggufFacts(h) {
|
|
2315
|
-
const
|
|
2316
|
-
const
|
|
2317
|
-
const arch =
|
|
2318
|
-
const ft =
|
|
2315
|
+
const s4 = (k) => typeof h.kv[k] === "string" ? h.kv[k] : null;
|
|
2316
|
+
const n3 = (k) => typeof h.kv[k] === "number" ? h.kv[k] : null;
|
|
2317
|
+
const arch = s4("general.architecture");
|
|
2318
|
+
const ft = n3("general.file_type");
|
|
2319
2319
|
const params = h.tensors.reduce((t, x) => t + x.dims.reduce((a, b) => a * b, 1), 0);
|
|
2320
2320
|
return {
|
|
2321
2321
|
architecture: arch,
|
|
2322
|
-
name:
|
|
2322
|
+
name: s4("general.name"),
|
|
2323
2323
|
quant: ft !== null ? FILE_TYPE_QUANT[ft] ?? `FTYPE_${ft}` : null,
|
|
2324
|
-
context_length: arch ?
|
|
2324
|
+
context_length: arch ? n3(`${arch}.context_length`) : null,
|
|
2325
2325
|
parameter_count: params > 0 ? params : null,
|
|
2326
|
-
split_count:
|
|
2327
|
-
split_index:
|
|
2328
|
-
type:
|
|
2326
|
+
split_count: n3("split.count") ?? 1,
|
|
2327
|
+
split_index: n3("split.no") ?? 0,
|
|
2328
|
+
type: s4("general.type"),
|
|
2329
2329
|
has_tokenizer: typeof h.kv["tokenizer.ggml.model"] === "string"
|
|
2330
2330
|
};
|
|
2331
2331
|
}
|
|
@@ -2366,16 +2366,16 @@ var isHelperGguf = (name) => HELPER_RE.test(name);
|
|
|
2366
2366
|
var isProjectorName = (name) => name.toLowerCase().includes("mmproj");
|
|
2367
2367
|
var quantFromName = (name) => QUANT_RE.exec(name.split("/").pop() ?? name)?.[1]?.toUpperCase() ?? null;
|
|
2368
2368
|
function splitSetProblems(names) {
|
|
2369
|
-
const shards = names.map(shardName).filter((
|
|
2369
|
+
const shards = names.map(shardName).filter((s4) => s4 !== null);
|
|
2370
2370
|
if (!shards.length) return [];
|
|
2371
|
-
const counts = [...new Set(shards.map((
|
|
2371
|
+
const counts = [...new Set(shards.map((s4) => s4.count))];
|
|
2372
2372
|
if (counts.length !== 1) return [`its parts disagree on how many parts there are (${counts.sort((a, b) => a - b).join(", ")})`];
|
|
2373
|
-
const
|
|
2374
|
-
const have = shards.map((
|
|
2375
|
-
const missing = Array.from({ length:
|
|
2373
|
+
const n3 = counts[0];
|
|
2374
|
+
const have = shards.map((s4) => s4.index);
|
|
2375
|
+
const missing = Array.from({ length: n3 }, (_, i) => i + 1).filter((i) => !have.includes(i));
|
|
2376
2376
|
const dupes = [...new Set(have.filter((i, k) => have.indexOf(i) !== k))].sort((a, b) => a - b);
|
|
2377
2377
|
const out = [];
|
|
2378
|
-
if (missing.length) out.push(`part${missing.length === 1 ? "" : "s"} ${missing.map((i) => `${i} of ${
|
|
2378
|
+
if (missing.length) out.push(`part${missing.length === 1 ? "" : "s"} ${missing.map((i) => `${i} of ${n3}`).join(", ")} ${missing.length === 1 ? "is" : "are"} missing`);
|
|
2379
2379
|
if (dupes.length) out.push(`part${dupes.length === 1 ? "" : "s"} ${dupes.join(", ")} appear${dupes.length === 1 ? "s" : ""} twice`);
|
|
2380
2380
|
return out;
|
|
2381
2381
|
}
|
|
@@ -2412,8 +2412,8 @@ async function validateModelSet(primary, siblings, env) {
|
|
|
2412
2412
|
let parts = [primary];
|
|
2413
2413
|
if (shard) {
|
|
2414
2414
|
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)}.` };
|
|
2415
|
-
const same = [primary, ...siblings.filter((
|
|
2416
|
-
const problems = splitSetProblems(same.map((
|
|
2415
|
+
const same = [primary, ...siblings.filter((s4) => s4 !== primary && shardName(s4.name)?.stem === shard.stem)];
|
|
2416
|
+
const problems = splitSetProblems(same.map((s4) => s4.name));
|
|
2417
2417
|
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).` };
|
|
2418
2418
|
parts = same.sort((a, b) => (shardName(a.name)?.index ?? 0) - (shardName(b.name)?.index ?? 0));
|
|
2419
2419
|
}
|
|
@@ -2446,7 +2446,7 @@ async function validateModelSet(primary, siblings, env) {
|
|
|
2446
2446
|
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?).` };
|
|
2447
2447
|
}
|
|
2448
2448
|
const quant = f.quant ?? quantFromName(primary.name) ?? "unknown";
|
|
2449
|
-
const projectorFiles = siblings.filter((
|
|
2449
|
+
const projectorFiles = siblings.filter((s4) => s4 !== primary && extOf(s4.name) === ".gguf" && isProjectorName(s4.name));
|
|
2450
2450
|
const pickedName = pickProjector(quant === "unknown" ? null : quant, projectorFiles.map((p) => p.name));
|
|
2451
2451
|
let projector = null;
|
|
2452
2452
|
const picked = projectorFiles.find((p) => p.name === pickedName);
|
|
@@ -2568,6 +2568,77 @@ function curatedList(models, { offeredOnly }) {
|
|
|
2568
2568
|
|
|
2569
2569
|
// models/catalog-reader.ts
|
|
2570
2570
|
import { sha256Hex } from "@ai-matrx/kit/sha256";
|
|
2571
|
+
|
|
2572
|
+
// models/device-models.ts
|
|
2573
|
+
var DEVICE_SERVED_VIA = "device";
|
|
2574
|
+
var DEVICE_FILE_USABLE_STATES = Object.freeze(["present"]);
|
|
2575
|
+
var DEVICE_OWN_FILE_USABLE_STATES = Object.freeze(["present", "unmatched"]);
|
|
2576
|
+
var s2 = (v) => typeof v === "string" ? v : "";
|
|
2577
|
+
var sn2 = (v) => typeof v === "string" ? v : null;
|
|
2578
|
+
var n = (v) => typeof v === "number" && Number.isFinite(v) ? v : typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : null;
|
|
2579
|
+
function toDeviceModelFile(r) {
|
|
2580
|
+
return {
|
|
2581
|
+
id: s2(r.id),
|
|
2582
|
+
app_instance_id: s2(r.app_instance_id),
|
|
2583
|
+
root_id: s2(r.root_id),
|
|
2584
|
+
rel_path: sn2(r.rel_path),
|
|
2585
|
+
size_bytes: n(r.size_bytes),
|
|
2586
|
+
sha256: sn2(r.sha256),
|
|
2587
|
+
artifact_id: sn2(r.artifact_id),
|
|
2588
|
+
state: s2(r.state),
|
|
2589
|
+
organization_id: sn2(r.organization_id)
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
async function readDeviceModelFiles(client, opts = {}) {
|
|
2593
|
+
const db = client;
|
|
2594
|
+
let q = db.schema("ai").from("device_model_file").select("*").is("deleted_at", null).in("state", opts.states ?? DEVICE_FILE_USABLE_STATES);
|
|
2595
|
+
if (opts.appInstanceIds) q = q.in("app_instance_id", opts.appInstanceIds);
|
|
2596
|
+
const { data, error } = await q;
|
|
2597
|
+
if (error) throw new Error(`ai.device_model_file: ${error.message}`);
|
|
2598
|
+
return (data ?? []).filter((r) => r.deleted_at == null).map(toDeviceModelFile);
|
|
2599
|
+
}
|
|
2600
|
+
function requiredArtifactIds(v) {
|
|
2601
|
+
const core = v.artifacts.filter((a) => a.required_for.includes("core"));
|
|
2602
|
+
return (core.length ? core : v.artifacts).map((a) => a.id);
|
|
2603
|
+
}
|
|
2604
|
+
function availableOnDevices(variants, files) {
|
|
2605
|
+
const usable = files.filter((f) => f.artifact_id && DEVICE_FILE_USABLE_STATES.includes(f.state));
|
|
2606
|
+
const out = /* @__PURE__ */ new Map();
|
|
2607
|
+
for (const v of variants) {
|
|
2608
|
+
const order = new Map(v.artifacts.map((a, i) => [a.id, i]));
|
|
2609
|
+
const required = requiredArtifactIds(v);
|
|
2610
|
+
const byDevice = /* @__PURE__ */ new Map();
|
|
2611
|
+
for (const f of usable)
|
|
2612
|
+
if (order.has(f.artifact_id)) {
|
|
2613
|
+
const list2 = byDevice.get(f.app_instance_id) ?? [];
|
|
2614
|
+
list2.push(f);
|
|
2615
|
+
byDevice.set(f.app_instance_id, list2);
|
|
2616
|
+
}
|
|
2617
|
+
const list = [];
|
|
2618
|
+
for (const device of [...byDevice.keys()].sort()) {
|
|
2619
|
+
const held = byDevice.get(device);
|
|
2620
|
+
const have = new Set(held.map((f) => f.artifact_id));
|
|
2621
|
+
if (!required.length || !required.every((id) => have.has(id))) continue;
|
|
2622
|
+
list.push({
|
|
2623
|
+
app_instance_id: device,
|
|
2624
|
+
variant_id: v.id,
|
|
2625
|
+
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)
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
out.set(v.id, list);
|
|
2629
|
+
}
|
|
2630
|
+
return out;
|
|
2631
|
+
}
|
|
2632
|
+
var DeviceModelUnavailableError = class extends Error {
|
|
2633
|
+
code;
|
|
2634
|
+
constructor(code, message) {
|
|
2635
|
+
super(message);
|
|
2636
|
+
this.name = "DeviceModelUnavailableError";
|
|
2637
|
+
this.code = code;
|
|
2638
|
+
}
|
|
2639
|
+
};
|
|
2640
|
+
|
|
2641
|
+
// models/catalog-reader.ts
|
|
2571
2642
|
var MODEL_CATALOG_KINDS = /* @__PURE__ */ new Set([
|
|
2572
2643
|
"llm_model",
|
|
2573
2644
|
"whisper_model",
|
|
@@ -2605,14 +2676,17 @@ function catalogFingerprint(catalog) {
|
|
|
2605
2676
|
const { catalog_fingerprint: _omit, ...rest } = catalog;
|
|
2606
2677
|
return sha256Hex(canonicalJson(rest));
|
|
2607
2678
|
}
|
|
2608
|
-
var
|
|
2609
|
-
var
|
|
2610
|
-
var
|
|
2679
|
+
var s3 = (v) => typeof v === "string" ? v : "";
|
|
2680
|
+
var sn3 = (v) => typeof v === "string" ? v : null;
|
|
2681
|
+
var n2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : null;
|
|
2611
2682
|
var live2 = (r) => r.deleted_at == null;
|
|
2612
2683
|
var byCodepoint2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
2613
|
-
var byId = (rows3) => new Map(rows3.map((r) => [
|
|
2684
|
+
var byId = (rows3) => new Map(rows3.map((r) => [s3(r.id), r]));
|
|
2614
2685
|
var strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
2615
2686
|
async function readModelCatalog(client, opts = {}) {
|
|
2687
|
+
return readCatalogWith(client, opts, null);
|
|
2688
|
+
}
|
|
2689
|
+
async function readCatalogWith(client, opts, preread) {
|
|
2616
2690
|
const db = client;
|
|
2617
2691
|
const offeredOnly = opts.offeredOnly ?? true;
|
|
2618
2692
|
const ai = db.schema("ai");
|
|
@@ -2630,20 +2704,29 @@ async function readModelCatalog(client, opts = {}) {
|
|
|
2630
2704
|
]);
|
|
2631
2705
|
const curated = curatedList(shapeCuratedModels(raw), { offeredOnly });
|
|
2632
2706
|
const listed = new Set(curated.map((m) => m.id));
|
|
2633
|
-
const variants = raw.variants.filter((v) => listed.has(
|
|
2707
|
+
const variants = raw.variants.filter((v) => listed.has(s3(v.model_id)) && (offeredOnly ? s3(v.review_state) === "offered" : isListedVariant(v)));
|
|
2634
2708
|
const entryIds = [...new Set(curated.flatMap((m) => m.variants.flatMap((v) => v.catalog_entry_ids)))].sort(byCodepoint2);
|
|
2635
2709
|
const warnings = [];
|
|
2636
|
-
const
|
|
2637
|
-
|
|
2710
|
+
const devices = opts.devices ? opts.devices === true ? {} : opts.devices : null;
|
|
2711
|
+
const [artifacts, entries, deviceFiles] = await Promise.all([
|
|
2712
|
+
variants.length ? all("model_artifact", (q) => q.in("variant_id", variants.map((v) => s3(v.id)))) : Promise.resolve([]),
|
|
2638
2713
|
entryIds.length ? Promise.resolve(db.schema("public").from("catalog_entries").select("id,kind").in("id", entryIds)).then(({ data, error }) => {
|
|
2639
2714
|
if (error) {
|
|
2640
2715
|
warnings.push(`public.catalog_entries: ${error.message} (models placed by what they do)`);
|
|
2641
2716
|
return [];
|
|
2642
2717
|
}
|
|
2643
2718
|
return data ?? [];
|
|
2644
|
-
}) : Promise.resolve([])
|
|
2719
|
+
}) : Promise.resolve([]),
|
|
2720
|
+
devices ? preread ? Promise.resolve(preread) : readDeviceModelFiles(client, devices.appInstanceIds ? { appInstanceIds: devices.appInstanceIds } : {}) : Promise.resolve(null)
|
|
2645
2721
|
]);
|
|
2646
|
-
const
|
|
2722
|
+
const placement2 = deviceFiles ? availableOnDevices(
|
|
2723
|
+
variants.map((v) => ({
|
|
2724
|
+
id: s3(v.id),
|
|
2725
|
+
artifacts: artifacts.filter((a) => s3(a.variant_id) === s3(v.id)).map((a) => ({ id: s3(a.id), required_for: strings(a.required_for) }))
|
|
2726
|
+
})),
|
|
2727
|
+
deviceFiles
|
|
2728
|
+
) : null;
|
|
2729
|
+
const kindOfEntry = new Map(entries.map((e) => [s3(e.id), s3(e.kind)]));
|
|
2647
2730
|
const stamps = [];
|
|
2648
2731
|
const stamp = (r) => {
|
|
2649
2732
|
if (r && typeof r.updated_at === "string") stamps.push(r.updated_at);
|
|
@@ -2651,84 +2734,87 @@ async function readModelCatalog(client, opts = {}) {
|
|
|
2651
2734
|
const modelById = byId(raw.models);
|
|
2652
2735
|
const providerById = byId(raw.providers);
|
|
2653
2736
|
const upstreamById = byId(upstreams);
|
|
2654
|
-
const presentationOf = new Map(raw.presentations.map((p) => [
|
|
2737
|
+
const presentationOf = new Map(raw.presentations.map((p) => [s3(p.model_id), p]));
|
|
2655
2738
|
const models = curated.flatMap((c) => {
|
|
2656
2739
|
const m = modelById.get(c.id);
|
|
2657
2740
|
if (!m) return [];
|
|
2658
2741
|
stamp(m);
|
|
2659
2742
|
const p = presentationOf.get(c.id);
|
|
2660
2743
|
stamp(p);
|
|
2661
|
-
const maker = providerById.get(
|
|
2744
|
+
const maker = providerById.get(s3(m.provider_id));
|
|
2662
2745
|
return [
|
|
2663
2746
|
{
|
|
2664
|
-
id:
|
|
2665
|
-
name:
|
|
2666
|
-
desktop_id:
|
|
2667
|
-
common_name:
|
|
2668
|
-
maker:
|
|
2669
|
-
maker_logo_file_id:
|
|
2670
|
-
description:
|
|
2747
|
+
id: s3(m.id),
|
|
2748
|
+
name: s3(m.name),
|
|
2749
|
+
desktop_id: sn3(m.metadata?.desktop_id),
|
|
2750
|
+
common_name: sn3(m.common_name),
|
|
2751
|
+
maker: sn3(maker?.name),
|
|
2752
|
+
maker_logo_file_id: sn3(maker?.logo_file_id),
|
|
2753
|
+
description: sn3(m.description),
|
|
2671
2754
|
capabilities: m.capabilities ?? {},
|
|
2672
2755
|
kind: catalogKindOf(c, kindOfEntry),
|
|
2673
2756
|
offered: c.offered,
|
|
2674
2757
|
updated_at: c.updated_at,
|
|
2675
2758
|
presentation: p ? {
|
|
2676
|
-
summary:
|
|
2677
|
-
symbol:
|
|
2678
|
-
color:
|
|
2679
|
-
shelf:
|
|
2680
|
-
shelf_title:
|
|
2681
|
-
shelf_subtitle:
|
|
2682
|
-
display_order:
|
|
2683
|
-
page:
|
|
2684
|
-
params_label:
|
|
2759
|
+
summary: sn3(p.summary),
|
|
2760
|
+
symbol: sn3(p.symbol),
|
|
2761
|
+
color: sn3(p.color),
|
|
2762
|
+
shelf: sn3(p.shelf),
|
|
2763
|
+
shelf_title: sn3(p.shelf_title),
|
|
2764
|
+
shelf_subtitle: sn3(p.shelf_subtitle),
|
|
2765
|
+
display_order: n2(p.display_order),
|
|
2766
|
+
page: sn3(p.page),
|
|
2767
|
+
params_label: sn3(p.params_label),
|
|
2685
2768
|
tags: strings(p.tags),
|
|
2686
2769
|
links: Array.isArray(p.links) ? p.links : [],
|
|
2687
|
-
thumbnail_file_id:
|
|
2688
|
-
hero_file_id:
|
|
2770
|
+
thumbnail_file_id: sn3(p.thumbnail_file_id),
|
|
2771
|
+
hero_file_id: sn3(p.hero_file_id),
|
|
2689
2772
|
gallery_file_ids: strings(p.gallery_file_ids).filter((f) => f.length > 0),
|
|
2690
2773
|
facts: p.facts && typeof p.facts === "object" && !Array.isArray(p.facts) ? p.facts : {}
|
|
2691
2774
|
} : null,
|
|
2692
|
-
variants: variants.filter((v) =>
|
|
2775
|
+
variants: variants.filter((v) => s3(v.model_id) === c.id).sort((a, b) => byCodepoint2(s3(a.key), s3(b.key))).map((v) => {
|
|
2693
2776
|
stamp(v);
|
|
2694
|
-
const up = upstreamById.get(
|
|
2777
|
+
const up = upstreamById.get(s3(v.upstream_id));
|
|
2695
2778
|
stamp(up);
|
|
2696
|
-
const arts = artifacts.filter((a) =>
|
|
2779
|
+
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)));
|
|
2697
2780
|
arts.forEach(stamp);
|
|
2698
2781
|
return {
|
|
2699
|
-
id:
|
|
2700
|
-
key:
|
|
2701
|
-
catalog_key: `${
|
|
2702
|
-
review_state:
|
|
2782
|
+
id: s3(v.id),
|
|
2783
|
+
key: s3(v.key),
|
|
2784
|
+
catalog_key: `${s3(m.name)}/${s3(v.key)}`,
|
|
2785
|
+
review_state: s3(v.review_state),
|
|
2703
2786
|
catalog_entry_ids: strings(v.metadata?.catalog_entry_ids),
|
|
2704
2787
|
is_default: v.is_default === true,
|
|
2705
|
-
format:
|
|
2706
|
-
quant:
|
|
2707
|
-
architecture:
|
|
2708
|
-
chat_template_kind:
|
|
2788
|
+
format: s3(v.format),
|
|
2789
|
+
quant: s3(v.quant),
|
|
2790
|
+
architecture: s3(v.architecture),
|
|
2791
|
+
chat_template_kind: s3(v.chat_template_kind),
|
|
2709
2792
|
os_arch: strings(v.os_arch),
|
|
2710
|
-
selection_rank:
|
|
2793
|
+
selection_rank: n2(v.selection_rank) ?? Number.MAX_SAFE_INTEGER,
|
|
2711
2794
|
// internal-bound: a row with no rank sorts last (the column is NOT NULL; this only orders a malformed row)
|
|
2712
|
-
license:
|
|
2713
|
-
gated:
|
|
2714
|
-
total_bytes:
|
|
2715
|
-
memory_bytes_min:
|
|
2716
|
-
memory_bytes_recommended:
|
|
2795
|
+
license: sn3(v.license_id),
|
|
2796
|
+
gated: s3(v.gated),
|
|
2797
|
+
total_bytes: n2(v.total_bytes),
|
|
2798
|
+
memory_bytes_min: n2(v.memory_bytes_min),
|
|
2799
|
+
memory_bytes_recommended: n2(v.memory_bytes_recommended),
|
|
2717
2800
|
tested_on_current_runtime: v.tested_on_current_runtime === true,
|
|
2718
|
-
min_app_version:
|
|
2719
|
-
upstream: up ? { kind:
|
|
2801
|
+
min_app_version: sn3(v.min_app_version),
|
|
2802
|
+
upstream: up ? { kind: s3(up.upstream_kind), repo: sn3(up.repo), revision: sn3(up.revision) } : null,
|
|
2720
2803
|
artifacts: arts.map((a) => ({
|
|
2721
|
-
role:
|
|
2722
|
-
path:
|
|
2723
|
-
url:
|
|
2724
|
-
size_bytes:
|
|
2725
|
-
sha256:
|
|
2804
|
+
role: s3(a.role),
|
|
2805
|
+
path: s3(a.path_in_repo),
|
|
2806
|
+
url: s3(a.url),
|
|
2807
|
+
size_bytes: n2(a.size_bytes) ?? 0,
|
|
2808
|
+
sha256: s3(a.sha256),
|
|
2726
2809
|
required_for: strings(a.required_for).sort(),
|
|
2727
|
-
shard_index:
|
|
2728
|
-
shard_count:
|
|
2810
|
+
shard_index: n2(a.shard_index),
|
|
2811
|
+
shard_count: n2(a.shard_count)
|
|
2729
2812
|
}))
|
|
2730
2813
|
};
|
|
2731
|
-
})
|
|
2814
|
+
}),
|
|
2815
|
+
...placement2 ? {
|
|
2816
|
+
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)) ?? [])
|
|
2817
|
+
} : {}
|
|
2732
2818
|
}
|
|
2733
2819
|
];
|
|
2734
2820
|
});
|
|
@@ -2736,36 +2822,36 @@ async function readModelCatalog(client, opts = {}) {
|
|
|
2736
2822
|
$schema: "matrx.model-catalog.snapshot/v1",
|
|
2737
2823
|
catalog_updated_at: "",
|
|
2738
2824
|
models,
|
|
2739
|
-
runtimes: runtimes.sort((a, b) => byCodepoint2(
|
|
2825
|
+
runtimes: runtimes.sort((a, b) => byCodepoint2(s3(a.key), s3(b.key))).map((rt) => {
|
|
2740
2826
|
stamp(rt);
|
|
2741
2827
|
return {
|
|
2742
|
-
key:
|
|
2743
|
-
name:
|
|
2744
|
-
upstream_repo:
|
|
2745
|
-
builds: builds.filter((b) =>
|
|
2828
|
+
key: s3(rt.key),
|
|
2829
|
+
name: s3(rt.name),
|
|
2830
|
+
upstream_repo: sn3(rt.upstream_repo),
|
|
2831
|
+
builds: builds.filter((b) => s3(b.runtime_id) === s3(rt.id)).map((b) => {
|
|
2746
2832
|
stamp(b);
|
|
2747
2833
|
const known = b.metadata?.known_architectures;
|
|
2748
2834
|
return {
|
|
2749
|
-
id:
|
|
2750
|
-
version:
|
|
2751
|
-
os:
|
|
2752
|
-
arch:
|
|
2753
|
-
accelerator:
|
|
2754
|
-
build_kind:
|
|
2755
|
-
url:
|
|
2756
|
-
size_bytes:
|
|
2757
|
-
sha256:
|
|
2835
|
+
id: s3(b.id),
|
|
2836
|
+
version: s3(b.runtime_version),
|
|
2837
|
+
os: s3(b.os),
|
|
2838
|
+
arch: s3(b.arch),
|
|
2839
|
+
accelerator: s3(b.accelerator),
|
|
2840
|
+
build_kind: s3(b.build_kind),
|
|
2841
|
+
url: sn3(b.url),
|
|
2842
|
+
size_bytes: n2(b.size_bytes),
|
|
2843
|
+
sha256: sn3(b.sha256),
|
|
2758
2844
|
is_active: b.is_active === true,
|
|
2759
|
-
min_app_version:
|
|
2845
|
+
min_app_version: sn3(b.min_app_version),
|
|
2760
2846
|
// The architectures this pinned llama-server build loads (runtime_build.metadata): the own-model judge reads it.
|
|
2761
2847
|
known_architectures: Array.isArray(known) ? known.map(String).sort() : null,
|
|
2762
|
-
compat: compat.filter((c) =>
|
|
2763
|
-
hardware_class:
|
|
2764
|
-
format:
|
|
2765
|
-
quant:
|
|
2766
|
-
architecture:
|
|
2767
|
-
chat_template_kind:
|
|
2768
|
-
status:
|
|
2848
|
+
compat: compat.filter((c) => s3(c.runtime_build_id) === s3(b.id)).map((c) => ({
|
|
2849
|
+
hardware_class: s3(c.hardware_class),
|
|
2850
|
+
format: s3(c.format),
|
|
2851
|
+
quant: s3(c.quant),
|
|
2852
|
+
architecture: s3(c.architecture),
|
|
2853
|
+
chat_template_kind: s3(c.chat_template_kind),
|
|
2854
|
+
status: s3(c.status)
|
|
2769
2855
|
}))
|
|
2770
2856
|
};
|
|
2771
2857
|
})
|
|
@@ -2777,6 +2863,37 @@ async function readModelCatalog(client, opts = {}) {
|
|
|
2777
2863
|
out.catalog_fingerprint = catalogFingerprint(out);
|
|
2778
2864
|
return out;
|
|
2779
2865
|
}
|
|
2866
|
+
async function resolveDeviceModel(client, req) {
|
|
2867
|
+
const files = await readDeviceModelFiles(client, {
|
|
2868
|
+
appInstanceIds: [req.app_instance_id],
|
|
2869
|
+
states: [.../* @__PURE__ */ new Set([...DEVICE_FILE_USABLE_STATES, ...DEVICE_OWN_FILE_USABLE_STATES])]
|
|
2870
|
+
});
|
|
2871
|
+
const own = files.find((f) => f.id === req.local_model_id);
|
|
2872
|
+
if (own) {
|
|
2873
|
+
if (own.artifact_id)
|
|
2874
|
+
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`);
|
|
2875
|
+
if (!DEVICE_OWN_FILE_USABLE_STATES.includes(own.state))
|
|
2876
|
+
throw new DeviceModelUnavailableError("file_not_usable", `own model file ${own.id} on computer ${req.app_instance_id} is ${own.state}`);
|
|
2877
|
+
return { own: true, file: own };
|
|
2878
|
+
}
|
|
2879
|
+
const usable = files.filter((f) => DEVICE_FILE_USABLE_STATES.includes(f.state));
|
|
2880
|
+
const catalog = await readCatalogWith(client, { offeredOnly: false, devices: { appInstanceIds: [req.app_instance_id] } }, usable);
|
|
2881
|
+
const model = catalog.models.find((m) => m.id === req.local_model_id);
|
|
2882
|
+
if (!model)
|
|
2883
|
+
throw new DeviceModelUnavailableError("unknown_model", `no catalog model or own model file ${req.local_model_id} on computer ${req.app_instance_id}`);
|
|
2884
|
+
const label = model.common_name || model.name;
|
|
2885
|
+
if (req.variant_id && !model.variants.some((v) => v.id === req.variant_id))
|
|
2886
|
+
throw new DeviceModelUnavailableError("unknown_variant", `${label} has no listed variant ${req.variant_id}`);
|
|
2887
|
+
const held = (model.available_on ?? []).filter((a) => a.app_instance_id === req.app_instance_id && (!req.variant_id || a.variant_id === req.variant_id));
|
|
2888
|
+
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));
|
|
2889
|
+
const pick = variants[0];
|
|
2890
|
+
if (!pick) {
|
|
2891
|
+
const which = req.variant_id ? ` (${model.variants.find((v) => v.id === req.variant_id).key})` : "";
|
|
2892
|
+
throw new DeviceModelUnavailableError("not_on_device", `computer ${req.app_instance_id} does not hold every required file of ${label}${which}`);
|
|
2893
|
+
}
|
|
2894
|
+
const byFileId = new Map(usable.map((f) => [f.id, f]));
|
|
2895
|
+
return { own: false, model, variant: pick.v, files: pick.a.device_model_file_ids.map((id) => byFileId.get(id)) };
|
|
2896
|
+
}
|
|
2780
2897
|
|
|
2781
2898
|
// models/fit.ts
|
|
2782
2899
|
var featuresOf = (c) => {
|
|
@@ -2861,6 +2978,10 @@ export {
|
|
|
2861
2978
|
CHAT_DEFAULT_MODEL_KNOB,
|
|
2862
2979
|
CONTENT_TYPES,
|
|
2863
2980
|
DEFAULT_CAPABILITIES,
|
|
2981
|
+
DEVICE_FILE_USABLE_STATES,
|
|
2982
|
+
DEVICE_OWN_FILE_USABLE_STATES,
|
|
2983
|
+
DEVICE_SERVED_VIA,
|
|
2984
|
+
DeviceModelUnavailableError,
|
|
2864
2985
|
EMPTY_MODEL_RECORDS,
|
|
2865
2986
|
EXCLUDED_REVIEW_STATES,
|
|
2866
2987
|
FEATURE_BUCKETS,
|
|
@@ -2881,6 +3002,7 @@ export {
|
|
|
2881
3002
|
TOP_TIER_LOCKED_TITLE,
|
|
2882
3003
|
acceptsTextInstructions,
|
|
2883
3004
|
announceModelCatalogChanged,
|
|
3005
|
+
availableOnDevices,
|
|
2884
3006
|
blendedPrice,
|
|
2885
3007
|
buildModelClassIndex,
|
|
2886
3008
|
buildSuccessionIndex,
|
|
@@ -2950,6 +3072,7 @@ export {
|
|
|
2950
3072
|
quantFromName,
|
|
2951
3073
|
rankVariants,
|
|
2952
3074
|
readCuratedRows,
|
|
3075
|
+
readDeviceModelFiles,
|
|
2953
3076
|
readGgufHeader,
|
|
2954
3077
|
readModelCatalog,
|
|
2955
3078
|
readModelIdentities,
|
|
@@ -2957,8 +3080,10 @@ export {
|
|
|
2957
3080
|
reconcileModelFavoriteIds,
|
|
2958
3081
|
reportModelDiagnostic,
|
|
2959
3082
|
requireCanonicalCapabilities,
|
|
3083
|
+
requiredArtifactIds,
|
|
2960
3084
|
requiredArtifacts,
|
|
2961
3085
|
resetSuccessionLogForTests,
|
|
3086
|
+
resolveDeviceModel,
|
|
2962
3087
|
resolveLiveModelId,
|
|
2963
3088
|
resolveModelMapKnob,
|
|
2964
3089
|
resolveNewAgentModelId,
|
|
@@ -2997,6 +3122,7 @@ export {
|
|
|
2997
3122
|
splitSetProblems,
|
|
2998
3123
|
takesInput,
|
|
2999
3124
|
toAuditRecord,
|
|
3125
|
+
toDeviceModelFile,
|
|
3000
3126
|
validateModelSet,
|
|
3001
3127
|
variantVerdict,
|
|
3002
3128
|
withModelClass
|