@ai-matrx/agents 0.59.275 → 0.59.277
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 +18 -1
- package/dist/models/index.cjs +154 -104
- package/dist/models/index.d.cts +17 -2
- package/dist/models/index.d.ts +17 -2
- package/dist/models/index.js +154 -104
- package/dist/models/react/index.d.cts +16 -1
- package/dist/models/react/index.d.ts +16 -1
- package/dist/versions/react/index.cjs +2 -1
- package/dist/versions/react/index.d.cts +4 -1
- package/dist/versions/react/index.d.ts +4 -1
- package/dist/versions/react/index.js +2 -1
- package/generated/llm-params.schema.json +12 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.59.
|
|
3
|
+
## 0.59.277
|
|
4
4
|
|
|
5
5
|
UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
|
|
6
6
|
Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/matrx-agents/src`.
|
|
7
7
|
|
|
8
|
+
## 0.59.276
|
|
9
|
+
|
|
10
|
+
- `./models`: one catalog edit costs a page at most two requests, however many model labels are mounted.
|
|
11
|
+
`records.refresh(listed?)` re-reads full records in ONE `in (...)` read, identities in ONE `in (...)`
|
|
12
|
+
read of only the ids no list row covers, and model class slices in ONE read; `createModelCatalog().refresh()`
|
|
13
|
+
passes its picker list's fresh `model_public` + `model_offering` rows (`ModelCatalogListRows`), which answer
|
|
14
|
+
the records' options list, identities and class index, so an edit is that one list read. `loadIdentity`
|
|
15
|
+
calls for ids in the same tick (N labels mounting) coalesce into one `in (...)` read, shared with
|
|
16
|
+
`loadIdentities`; a name-shaped key still reads alone. Live 2026-10-24 (web f8dc2956): one rename cost the
|
|
17
|
+
chat page a list read plus one single-id `model_definition` read per label. Guard:
|
|
18
|
+
`models/react/catalog-changed-request-count.test.tsx`. Consumer action: none (the argument is optional).
|
|
19
|
+
|
|
20
|
+
## 0.59.275
|
|
21
|
+
|
|
22
|
+
Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff 6393a0ff9a cf96f35563 -- apps/shared/matrx-agents/src`
|
|
23
|
+
has no shipped-source changes outside tests, fixtures and docs). No consumer action required.
|
|
24
|
+
|
|
8
25
|
## 0.59.274
|
|
9
26
|
|
|
10
27
|
- `./models`: a burst of `catalog_changed` is ONE re-read. New `coalesceModelCatalogChanges(deliver, options?)`:
|
package/dist/models/index.cjs
CHANGED
|
@@ -1021,6 +1021,9 @@ function withIdLists(state2) {
|
|
|
1021
1021
|
const sort = (ids) => ids.sort((a, b) => label(a).localeCompare(label(b)));
|
|
1022
1022
|
return { ...state2, activeIds: sort(active), deprecatedIds: sort(deprecated) };
|
|
1023
1023
|
}
|
|
1024
|
+
var IDENTITY_COLUMNS = "id, name, common_name, is_deprecated";
|
|
1025
|
+
var OPTION_COLUMN_LIST = ["id", "name", "common_name", "maker", "cost_rating", "speed_rating", "is_primary", "capabilities", "is_deprecated", "retired_at", "successor_id"];
|
|
1026
|
+
var OPTION_COLUMNS = OPTION_COLUMN_LIST.join(", ");
|
|
1024
1027
|
var MODEL_CONFIG_COLUMNS = "id, name, common_name, maker, cost_rating, speed_rating, is_premium, is_primary, context_window, max_tokens, capabilities, controls, constraints, is_deprecated, retired_at, successor_id";
|
|
1025
1028
|
function createModelRecords(config) {
|
|
1026
1029
|
if (!config?.client) throw new Error("createModelRecords: `client` (a Supabase client) is required.");
|
|
@@ -1045,13 +1048,10 @@ function createModelRecords(config) {
|
|
|
1045
1048
|
inFlight.set(key, p);
|
|
1046
1049
|
return p;
|
|
1047
1050
|
};
|
|
1048
|
-
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
if (
|
|
1052
|
-
const row = data;
|
|
1053
|
-
if (!row) throw new Error(`AI model ${modelId} is not available (ai.${relation} has no such row)`);
|
|
1054
|
-
if (!row.id || !row.name) throw new Error(`ai.${relation} returned a row without id/name for model ${modelId}`);
|
|
1051
|
+
function toFullRecord(raw, relation, asked) {
|
|
1052
|
+
const row = raw;
|
|
1053
|
+
if (!row) throw new Error(`AI model ${asked} is not available (ai.${relation} has no such row)`);
|
|
1054
|
+
if (!row.id || !row.name) throw new Error(`ai.${relation} returned a row without id/name for model ${asked}`);
|
|
1055
1055
|
const read = {
|
|
1056
1056
|
...row,
|
|
1057
1057
|
id: row.id,
|
|
@@ -1061,74 +1061,146 @@ function createModelRecords(config) {
|
|
|
1061
1061
|
};
|
|
1062
1062
|
return normalizeModel(read);
|
|
1063
1063
|
}
|
|
1064
|
-
async function
|
|
1064
|
+
async function readFullModel(modelId) {
|
|
1065
|
+
const relation = await isSignedIn() ? "model_config" : "model_public";
|
|
1066
|
+
const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
|
|
1067
|
+
if (error) throw error;
|
|
1068
|
+
return toFullRecord(data, relation, modelId);
|
|
1069
|
+
}
|
|
1070
|
+
async function readOfferings(modelIds) {
|
|
1065
1071
|
const q = ai().from("model_offering").select("model_id, offering_id, served_via, served_via_endpoint_id, priority");
|
|
1066
|
-
const { data, error } = await (
|
|
1072
|
+
const { data, error } = await (modelIds ? modelIds.length === 1 ? q.eq("model_id", modelIds[0]) : q.in("model_id", modelIds) : q);
|
|
1067
1073
|
if (error) throw error;
|
|
1068
1074
|
return data ?? [];
|
|
1069
1075
|
}
|
|
1070
|
-
|
|
1076
|
+
const mergeClassSlice = (rows3) => update((s4) => {
|
|
1077
|
+
if (s4.modelClassStatus === "succeeded") return s4;
|
|
1078
|
+
const slice = buildModelClassIndex(rows3);
|
|
1079
|
+
const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
|
|
1080
|
+
return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
|
|
1081
|
+
});
|
|
1082
|
+
let identityTick = null;
|
|
1083
|
+
async function readIdentitiesNow(wanted) {
|
|
1084
|
+
try {
|
|
1085
|
+
const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).in("id", wanted);
|
|
1086
|
+
if (error) throw error;
|
|
1087
|
+
const rows3 = Array.isArray(data) ? data : [];
|
|
1088
|
+
const got = new Set(rows3.map((r) => r.id));
|
|
1089
|
+
const missing = wanted.filter((id) => !got.has(id));
|
|
1090
|
+
if (missing.length > 0) errorSink({ code: "model-records-identity-failed", message: `AI model ${missing.join(", ")} is not available (ai.model_definition has no such row)`, context: { modelIds: missing } });
|
|
1091
|
+
update((s4) => ({
|
|
1092
|
+
...s4,
|
|
1093
|
+
identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
|
|
1094
|
+
identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"])) }
|
|
1095
|
+
}));
|
|
1096
|
+
} catch (err) {
|
|
1097
|
+
errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
|
|
1098
|
+
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) } }));
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function queueIdentity(id) {
|
|
1102
|
+
if (!identityTick) {
|
|
1103
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1104
|
+
const done = Promise.resolve().then(() => {
|
|
1105
|
+
identityTick = null;
|
|
1106
|
+
return readIdentitiesNow([...ids]);
|
|
1107
|
+
});
|
|
1108
|
+
identityTick = { ids, done };
|
|
1109
|
+
void done.finally(() => {
|
|
1110
|
+
for (const queued of ids) if (inFlight.get(`identity:${queued}`) === done) inFlight.delete(`identity:${queued}`);
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
identityTick.ids.add(id);
|
|
1114
|
+
inFlight.set(`identity:${id}`, identityTick.done);
|
|
1115
|
+
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [id]: "loading" } }));
|
|
1116
|
+
return identityTick.done;
|
|
1117
|
+
}
|
|
1118
|
+
async function rereadKnown(listed) {
|
|
1071
1119
|
const known = state2;
|
|
1072
1120
|
const jobs = [];
|
|
1073
1121
|
const fail = (code, message, context) => errorSink({ code, message: `Catalog refresh: ${message}`, ...context ? { context } : {} });
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1122
|
+
const fed = listed ? listed.then((f) => f, () => null) : Promise.resolve(null);
|
|
1123
|
+
const optionsWereRead = known.fetchScope === "active" || known.fetchScope === "all";
|
|
1124
|
+
const freshOptions = optionsWereRead ? fed.then((f) => f ? optionRowsOf(f.publicRows, f.offeringRows) : readOptions()) : Promise.resolve(null);
|
|
1125
|
+
jobs.push(
|
|
1126
|
+
freshOptions.then(
|
|
1127
|
+
(rows3) => {
|
|
1128
|
+
if (!rows3) return;
|
|
1129
|
+
update((s4) => {
|
|
1078
1130
|
const entities = { ...s4.entities };
|
|
1079
1131
|
for (const row of rows3) {
|
|
1080
1132
|
const existing = entities[row.id];
|
|
1081
1133
|
entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: existing?._fetchType === "full" ? "full" : "options" };
|
|
1082
1134
|
}
|
|
1083
1135
|
return withIdLists({ ...s4, entities, lastFetched: Date.now(), error: null });
|
|
1084
|
-
})
|
|
1085
|
-
|
|
1086
|
-
)
|
|
1087
|
-
)
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1136
|
+
});
|
|
1137
|
+
},
|
|
1138
|
+
(err) => fail("model-records-refresh-options-failed", messageOf(err))
|
|
1139
|
+
)
|
|
1140
|
+
);
|
|
1141
|
+
const fullIds = Object.entries(known.entities).filter(([, r]) => r._fetchType === "full").map(([id]) => id);
|
|
1142
|
+
if (fullIds.length > 0) {
|
|
1091
1143
|
jobs.push(
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1144
|
+
(async () => {
|
|
1145
|
+
const relation = await isSignedIn() ? "model_config" : "model_public";
|
|
1146
|
+
const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).in("id", fullIds);
|
|
1147
|
+
if (error) throw error;
|
|
1148
|
+
const byId2 = new Map((Array.isArray(data) ? data : []).map((r) => [r.id, r]));
|
|
1149
|
+
for (const id of fullIds) {
|
|
1150
|
+
try {
|
|
1151
|
+
const fresh = toFullRecord(byId2.get(id) ?? null, relation, id);
|
|
1152
|
+
update((s4) => {
|
|
1153
|
+
const existing = s4.entities[fresh.id];
|
|
1154
|
+
return withIdLists({ ...s4, entities: { ...s4.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
|
|
1155
|
+
});
|
|
1156
|
+
} catch (err) {
|
|
1157
|
+
fail("model-records-refresh-model-failed", `${id}: ${messageOf(err)}`, { modelId: id });
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
})().catch((err) => fail("model-records-refresh-model-failed", `${fullIds.join(", ")}: ${messageOf(err)}`, { modelIds: fullIds }))
|
|
1099
1161
|
);
|
|
1100
1162
|
}
|
|
1101
|
-
|
|
1163
|
+
const identityKeys = Object.entries(known.identityById);
|
|
1164
|
+
if (identityKeys.length > 0) {
|
|
1102
1165
|
jobs.push(
|
|
1103
|
-
|
|
1104
|
-
|
|
1166
|
+
(async () => {
|
|
1167
|
+
const [f, opts] = await Promise.all([fed, freshOptions.catch(() => null)]);
|
|
1168
|
+
const covered = /* @__PURE__ */ new Map();
|
|
1169
|
+
for (const r of [...f?.publicRows ?? [], ...opts ?? []]) {
|
|
1170
|
+
if (typeof r.id === "string" && typeof r.name === "string") {
|
|
1171
|
+
covered.set(r.id, { id: r.id, name: r.name, common_name: r.common_name ?? null, is_deprecated: r.is_deprecated ?? null });
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
const wanted = [...new Set(identityKeys.map(([, row]) => row.id))].filter((id) => !covered.has(id) && known.entities[id]?._fetchType !== "full");
|
|
1175
|
+
if (wanted.length > 0) {
|
|
1176
|
+
const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).in("id", wanted);
|
|
1105
1177
|
if (error) throw error;
|
|
1106
|
-
|
|
1178
|
+
for (const row of Array.isArray(data) ? data : []) covered.set(row.id, row);
|
|
1179
|
+
}
|
|
1180
|
+
const next = {};
|
|
1181
|
+
for (const [key, row] of identityKeys) {
|
|
1182
|
+
const fresh = covered.get(row.id);
|
|
1183
|
+
if (fresh) next[key] = fresh;
|
|
1107
1184
|
}
|
|
1108
|
-
|
|
1185
|
+
if (Object.keys(next).length > 0) update((s4) => ({ ...s4, identityById: { ...s4.identityById, ...next } }));
|
|
1186
|
+
})().catch((err) => fail("model-records-refresh-identity-failed", messageOf(err)))
|
|
1109
1187
|
);
|
|
1110
1188
|
}
|
|
1111
1189
|
if (known.modelClassStatus === "succeeded") {
|
|
1112
1190
|
jobs.push(
|
|
1113
|
-
readOfferings().then(
|
|
1191
|
+
fed.then((f) => f?.offeringRows ?? readOfferings()).then(
|
|
1114
1192
|
(rows3) => update((s4) => ({ ...s4, modelClassIndex: buildModelClassIndex(rows3) })),
|
|
1115
1193
|
(err) => fail("model-records-refresh-classes-failed", messageOf(err))
|
|
1116
1194
|
)
|
|
1117
1195
|
);
|
|
1118
|
-
} else {
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
|
|
1127
|
-
}),
|
|
1128
|
-
(err) => fail("model-records-refresh-classes-failed", `${id}: ${messageOf(err)}`, { modelId: id })
|
|
1129
|
-
)
|
|
1130
|
-
);
|
|
1131
|
-
}
|
|
1196
|
+
} else if (classesReadFor.size > 0) {
|
|
1197
|
+
const ids = [...classesReadFor];
|
|
1198
|
+
jobs.push(
|
|
1199
|
+
fed.then((f) => f ? f.offeringRows.filter((o) => o.model_id !== null && classesReadFor.has(o.model_id)) : readOfferings(ids)).then(
|
|
1200
|
+
(rows3) => mergeClassSlice(rows3),
|
|
1201
|
+
(err) => fail("model-records-refresh-classes-failed", `${ids.join(", ")}: ${messageOf(err)}`, { modelIds: ids })
|
|
1202
|
+
)
|
|
1203
|
+
);
|
|
1132
1204
|
}
|
|
1133
1205
|
for (const [key, cfg] of Object.entries(known.classConfigByOffering)) {
|
|
1134
1206
|
const offeringId = key.slice(cfg.modelId.length + 1);
|
|
@@ -1146,16 +1218,29 @@ function createModelRecords(config) {
|
|
|
1146
1218
|
}
|
|
1147
1219
|
let refreshing = null;
|
|
1148
1220
|
let refreshAgain = null;
|
|
1149
|
-
function refreshAll() {
|
|
1221
|
+
function refreshAll(listed) {
|
|
1150
1222
|
if (refreshing) return refreshAgain ??= refreshing.then(() => {
|
|
1151
1223
|
refreshAgain = null;
|
|
1152
1224
|
return refreshAll();
|
|
1153
1225
|
});
|
|
1154
|
-
refreshing = rereadKnown().finally(() => {
|
|
1226
|
+
refreshing = rereadKnown(listed).finally(() => {
|
|
1155
1227
|
refreshing = null;
|
|
1156
1228
|
});
|
|
1157
1229
|
return refreshing;
|
|
1158
1230
|
}
|
|
1231
|
+
function optionRowsOf(publicRows, offeringRows) {
|
|
1232
|
+
const routable = new Set(offeringRows.map((r) => r.model_id).filter((id) => typeof id === "string" && id.length > 0));
|
|
1233
|
+
return publicRows.filter((r) => typeof r.id === "string" && typeof r.name === "string" && routable.has(r.id)).map((r) => {
|
|
1234
|
+
const picked = {};
|
|
1235
|
+
for (const c of OPTION_COLUMN_LIST) if (c in r) picked[c] = r[c];
|
|
1236
|
+
return {
|
|
1237
|
+
...picked,
|
|
1238
|
+
id: r.id,
|
|
1239
|
+
name: r.name,
|
|
1240
|
+
capabilities: requireCanonicalCapabilities(r.capabilities, { modelId: r.id, modelName: r.name })
|
|
1241
|
+
};
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1159
1244
|
async function readOptions() {
|
|
1160
1245
|
const { data: offeringRows, error: offeringError } = await ai().from("model_offering").select("model_id");
|
|
1161
1246
|
if (offeringError) throw offeringError;
|
|
@@ -1165,12 +1250,9 @@ function createModelRecords(config) {
|
|
|
1165
1250
|
)
|
|
1166
1251
|
];
|
|
1167
1252
|
if (routableIds.length === 0) return [];
|
|
1168
|
-
const { data, error } = await ai().from("model_public").select(
|
|
1253
|
+
const { data, error } = await ai().from("model_public").select(OPTION_COLUMNS).in("id", routableIds).order("common_name", { ascending: true, nullsFirst: false });
|
|
1169
1254
|
if (error) throw error;
|
|
1170
|
-
return (data ?? []
|
|
1171
|
-
...r,
|
|
1172
|
-
capabilities: requireCanonicalCapabilities(r.capabilities, { modelId: r.id, modelName: r.name })
|
|
1173
|
-
}));
|
|
1255
|
+
return optionRowsOf(data ?? [], routableIds.map((model_id) => ({ model_id })));
|
|
1174
1256
|
}
|
|
1175
1257
|
const api = {
|
|
1176
1258
|
getState: () => state2,
|
|
@@ -1327,10 +1409,11 @@ function createModelRecords(config) {
|
|
|
1327
1409
|
if (!modelId || state2.entities[modelId] || state2.identityById[modelId] || status === "loading" || status === "succeeded" || status === "failed") {
|
|
1328
1410
|
return inFlight.get(`identity:${modelId}`) ?? Promise.resolve();
|
|
1329
1411
|
}
|
|
1412
|
+
if (catalogKeyColumn(modelId) === "id") return queueIdentity(modelId);
|
|
1330
1413
|
return once(`identity:${modelId}`, async () => {
|
|
1331
1414
|
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "loading" } }));
|
|
1332
1415
|
try {
|
|
1333
|
-
const { data, error } = await ai().from("model_definition").select(
|
|
1416
|
+
const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
|
|
1334
1417
|
if (error) throw error;
|
|
1335
1418
|
if (!data) throw new Error(`AI model ${modelId} is not available (ai.model_definition has no such row)`);
|
|
1336
1419
|
const row = data;
|
|
@@ -1346,52 +1429,10 @@ function createModelRecords(config) {
|
|
|
1346
1429
|
});
|
|
1347
1430
|
},
|
|
1348
1431
|
loadIdentities(modelIds) {
|
|
1349
|
-
const asked = (id) => {
|
|
1350
|
-
const status = state2.identityStatusById[id];
|
|
1351
|
-
return Boolean(state2.entities[id] || state2.identityById[id] || status === "loading" || status === "succeeded" || status === "failed");
|
|
1352
|
-
};
|
|
1353
1432
|
const ids = [...new Set(modelIds.filter((id) => Boolean(id)))];
|
|
1354
|
-
|
|
1355
|
-
const wanted = [];
|
|
1356
|
-
for (const id of ids) {
|
|
1357
|
-
if (catalogKeyColumn(id) !== "id") waits.push(api.loadIdentity(id));
|
|
1358
|
-
else if (asked(id)) waits.push(inFlight.get(`identity:${id}`) ?? Promise.resolve());
|
|
1359
|
-
else wanted.push(id);
|
|
1360
|
-
}
|
|
1361
|
-
if (wanted.length > 0) {
|
|
1362
|
-
const marked = Object.fromEntries(wanted.map((id) => [id, "loading"]));
|
|
1363
|
-
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...marked } }));
|
|
1364
|
-
const run = (async () => {
|
|
1365
|
-
try {
|
|
1366
|
-
const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").in("id", wanted);
|
|
1367
|
-
if (error) throw error;
|
|
1368
|
-
const rows3 = Array.isArray(data) ? data : [];
|
|
1369
|
-
const got = new Set(rows3.map((r) => r.id));
|
|
1370
|
-
update((s4) => ({
|
|
1371
|
-
...s4,
|
|
1372
|
-
identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
|
|
1373
|
-
identityStatusById: {
|
|
1374
|
-
...s4.identityStatusById,
|
|
1375
|
-
...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"]))
|
|
1376
|
-
}
|
|
1377
|
-
}));
|
|
1378
|
-
} catch (err) {
|
|
1379
|
-
errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
|
|
1380
|
-
update((s4) => ({
|
|
1381
|
-
...s4,
|
|
1382
|
-
identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) }
|
|
1383
|
-
}));
|
|
1384
|
-
}
|
|
1385
|
-
})();
|
|
1386
|
-
for (const id of wanted) inFlight.set(`identity:${id}`, run);
|
|
1387
|
-
void run.finally(() => {
|
|
1388
|
-
for (const id of wanted) if (inFlight.get(`identity:${id}`) === run) inFlight.delete(`identity:${id}`);
|
|
1389
|
-
});
|
|
1390
|
-
waits.push(run);
|
|
1391
|
-
}
|
|
1392
|
-
return Promise.all(waits).then(() => void 0);
|
|
1433
|
+
return Promise.all(ids.map((id) => api.loadIdentity(id))).then(() => void 0);
|
|
1393
1434
|
},
|
|
1394
|
-
refresh: () => refreshAll(),
|
|
1435
|
+
refresh: (listed) => refreshAll(listed),
|
|
1395
1436
|
seedModel({ modelId, config: config2, offerings }) {
|
|
1396
1437
|
if (!modelId) return;
|
|
1397
1438
|
if (!classesReadFor.has(modelId) && state2.modelClassStatus !== "succeeded") {
|
|
@@ -1840,7 +1881,7 @@ function aiRpc(client, fn) {
|
|
|
1840
1881
|
if (!scope.rpc) throw new Error(`The model catalog client's schema("ai") has no rpc(), so ${fn} cannot be called.`);
|
|
1841
1882
|
return scope.rpc(fn);
|
|
1842
1883
|
}
|
|
1843
|
-
async function readCatalog(client, variant) {
|
|
1884
|
+
async function readCatalog(client, variant, onListRows) {
|
|
1844
1885
|
if (variant === "admin") {
|
|
1845
1886
|
const [rpcRes, offeringsRes] = await Promise.all([
|
|
1846
1887
|
aiRpc(client, "admin_model_catalog"),
|
|
@@ -1891,6 +1932,7 @@ async function readCatalog(client, variant) {
|
|
|
1891
1932
|
]);
|
|
1892
1933
|
if (publicRes.error) throw publicRes.error;
|
|
1893
1934
|
if (offeringRes.error) throw offeringRes.error;
|
|
1935
|
+
onListRows?.({ publicRows: rows(publicRes.data), offeringRows: rows(offeringRes.data) });
|
|
1894
1936
|
const tiersByModel = /* @__PURE__ */ new Map();
|
|
1895
1937
|
for (const r of rows(offeringRes.data)) {
|
|
1896
1938
|
const modelId = str(r.model_id);
|
|
@@ -1930,14 +1972,14 @@ function createModelCatalog(config) {
|
|
|
1930
1972
|
state2 = { ...state2, [variant]: next };
|
|
1931
1973
|
for (const l of [...listeners]) l();
|
|
1932
1974
|
};
|
|
1933
|
-
const run = (variant) => {
|
|
1975
|
+
const run = (variant, onListRows) => {
|
|
1934
1976
|
const pending = inFlight[variant];
|
|
1935
1977
|
if (pending) return pending;
|
|
1936
1978
|
const prior = state2[variant];
|
|
1937
1979
|
if (prior.status === "idle" || prior.status === "error") set(variant, { ...prior, status: "loading", error: null });
|
|
1938
1980
|
const job = (async () => {
|
|
1939
1981
|
try {
|
|
1940
|
-
const models = await readCatalog(client, variant);
|
|
1982
|
+
const models = await readCatalog(client, variant, onListRows);
|
|
1941
1983
|
set(variant, { status: "ready", models, error: null, loadedAt: Date.now() });
|
|
1942
1984
|
} catch (err) {
|
|
1943
1985
|
const message = messageOf2(err);
|
|
@@ -1957,7 +1999,15 @@ function createModelCatalog(config) {
|
|
|
1957
1999
|
});
|
|
1958
2000
|
const refresh = async () => {
|
|
1959
2001
|
const variants = Object.keys(state2).filter((v) => state2[v].status !== "idle");
|
|
1960
|
-
|
|
2002
|
+
let listed;
|
|
2003
|
+
const runs = variants.map((v) => {
|
|
2004
|
+
if (v !== "user" || inFlight.user) return run(v);
|
|
2005
|
+
let give = () => {
|
|
2006
|
+
};
|
|
2007
|
+
listed = new Promise((resolve) => give = resolve);
|
|
2008
|
+
return run("user", (rows3) => give(rows3)).finally(() => give(null));
|
|
2009
|
+
});
|
|
2010
|
+
await Promise.all([records.refresh(listed), ...runs]);
|
|
1961
2011
|
};
|
|
1962
2012
|
const stopFollowing = config.followChanges === false ? () => {
|
|
1963
2013
|
} : onModelCatalogChanged(() => void refresh());
|
package/dist/models/index.d.cts
CHANGED
|
@@ -600,6 +600,15 @@ interface ModelRecord {
|
|
|
600
600
|
published_to_web_by: string | null;
|
|
601
601
|
}
|
|
602
602
|
type ModelIdentityRow = Pick<ModelRecord, "id" | "name" | "common_name" | "is_deprecated">;
|
|
603
|
+
/**
|
|
604
|
+
* What the picker list's read of the catalog returned, raw: every `ai.model_public` row (with at least the
|
|
605
|
+
* options columns) and every `ai.model_offering` row (`ModelOfferingRow` columns). `records.refresh(listed)`
|
|
606
|
+
* answers the options list, the identities and the class index from it instead of reading them again.
|
|
607
|
+
*/
|
|
608
|
+
interface ModelCatalogListRows {
|
|
609
|
+
publicRows: Array<Record<string, unknown>>;
|
|
610
|
+
offeringRows: ModelOfferingRow[];
|
|
611
|
+
}
|
|
603
612
|
type ModelClassConfig = {
|
|
604
613
|
modelId: string;
|
|
605
614
|
controls: ModelJson | null;
|
|
@@ -693,8 +702,14 @@ interface ModelRecords {
|
|
|
693
702
|
* the class index and slices, every pinned class's controls — and swap each in when it answers. The old
|
|
694
703
|
* rows stay on screen until then (no blank pill, no flicker); a failed re-read keeps them and reports
|
|
695
704
|
* through the error sink. Reads nothing that was never read. Never throws.
|
|
705
|
+
*
|
|
706
|
+
* Few requests, however many labels are mounted: full records are ONE `in (...)` read, identities are
|
|
707
|
+
* ONE `in (...)` read of only the ids no list row covers, model class slices are ONE read. `listed` is
|
|
708
|
+
* the picker list's own fresh re-read of `ai.model_public` + `ai.model_offering` (the catalog passes it,
|
|
709
|
+
* `createModelCatalog().refresh()`): when given, the options list, the identities it covers and the class
|
|
710
|
+
* index are answered from it, so one edit costs the page that one list read.
|
|
696
711
|
*/
|
|
697
|
-
refresh(): Promise<void>;
|
|
712
|
+
refresh(listed?: Promise<ModelCatalogListRows | null>): Promise<void>;
|
|
698
713
|
/**
|
|
699
714
|
* Answer `loadModel` + `loadModelClasses` for one model from rows a batched
|
|
700
715
|
* route read already returned (`chat.route_seed`): its `ai.model_config` row
|
|
@@ -2134,4 +2149,4 @@ interface ModelCatalogVisibilityOptions {
|
|
|
2134
2149
|
declare function followModelCatalogVisibility(options: ModelCatalogVisibilityOptions): () => void;
|
|
2135
2150
|
|
|
2136
2151
|
export { CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, DEFAULT_CAPABILITIES, DEVICE_FILE_USABLE_STATES, DEVICE_OWN_FILE_USABLE_STATES, DEVICE_RULE_TWIN_FIXTURE, DEVICE_SERVED_SETTINGS_KEY, DEVICE_SERVED_VIA, DeviceModelUnavailableError, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, GgufError, INPUT_MODALITIES, INTERACTION_MODES, LLM_INTERACTIONS, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, NEW_AGENT_DEFAULT_MODEL_KNOB, OUTPUT_MODALITIES, SERVICE_LABEL, SERVICE_LABEL_PLURAL, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, UNKNOWN_MODEL_KIND, acceptsTextInstructions, announceModelCatalogChanged, availableOnDevices, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, catalogKindVerdict, cheapestAdequateModel, classConfigKey, coalesceModelCatalogChanges, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, followModelCatalogChanges, followModelCatalogVisibility, formatMemoryBytes, fromDiscovery, ggufEngine, ggufFacts, groupFeatures, hardwareClassesOf, hasCompatibleDecisionInteraction, isChatModelCapability, isContentType, isConversationalModelCapability, isDecisionModelCapability, isFeatureKey, isHelperGguf, isInteractionMode, isListedVariant, isLocalModelCatalog, isManagedAgentModelCapability, isModelCatalogChange, isProjectorName, isSupersedable, isTopTierRating, judgeHeaderFacts, mergeAuditRecordIntoCapabilities, missingShards, modelCatalogRevision, modelClassKnobFor, modelClassName, modelClassPinOf, modelDisplayLabel, modelsForSelectionPurpose, normalizeArch, normalizeModel, normalizeModels, normalizeOs, normalizePromptSettings, onModelCatalogChanged, orderCatalogGroups, orderCatalogModels, outputFormatControlKey, parseCapabilities, pickProjector, priceTierRating, quantFromName, rankVariants, readCuratedRows, readDeviceModelFiles, readGgufHeader, readModelCatalog, readModelCatalogStamp, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifactIds, requiredArtifacts, resetSuccessionLogForTests, resolveDeviceModel, resolveLiveModelId, resolveModelMapKnob, resolveNewAgentModelId, resolvePreferredModelChoice, resolveSuccessor, runtimesOnEngine, seesImages, selectActiveModels, selectActiveModelsReady, selectAllModelOptions, selectAllModels, selectAllModelsReady, selectDeprecatedModels, selectDeprecatedModelsReady, selectModelById, selectModelClassConfig, selectModelClassName, selectModelControls, selectModelDetailError, selectModelFetchType, selectModelFullyLoaded, selectModelIdentityById, selectModelIdentityLookupStatus, selectModelLabelById, selectModelLabelWithClass, selectModelNameById, selectModelOptions, selectVariant, serviceCountLabel, setModelDiagnosticsSink, shapeCuratedModels, shardFileName, shardName, speedRatingLabel, speedRatingWord, splitSetProblems, takesInput, toAuditRecord, toDeviceModelFile, validateModelSet, variantVerdict, withModelClass };
|
|
2137
|
-
export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogKindVerdict, CatalogModel, CatalogTier, CheapestAdequateOptions, CompatStatus, ContentType, CuratedCatalogClient, CuratedModel, CuratedPresentation, CuratedRows, CuratedVariant, DecidedBy, DeviceAvailability, DeviceGraphics, DeviceHardware, DeviceModelFile, DeviceModelResolution, DeviceServedSettings, DiscoveryAmbiguity, DiscoveryArtifact, DiscoveryMedia, DiscoveryResult, DiscoveryRuntime, DiscoverySource, DiscoveryVariant, FeatureBucket, FeatureBucketMeta, FeatureKey, FitCapabilities, GgufHeader, GgufRead, GgufTensor, GroupedFeatures, HardwareClass, HardwareClasses, HeaderFlag, HeaderJudgement, HeaderRefusal, Interaction, InteractionMode, LocalArtifact, LocalCatalogClient, LocalCatalogQuery, LocalCompatRow, LocalModel, LocalModelCatalog, LocalPresentation, LocalRuntime, LocalRuntimeBuild, LocalVariant, Modality, ModelCapabilities, ModelCatalog, ModelCatalogChangeMeta, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogCoalesceOptions, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogPgError, ModelCatalogPgResult, ModelCatalogQuery, ModelCatalogState, ModelCatalogVariant, ModelCatalogVariantState, ModelCatalogVisibilityOptions, ModelClassConfig, ModelClassEntry, ModelClassIndex, ModelDiagnosticEntry, ModelDiagnosticsSink, ModelFavorites, ModelFavoritesCache, ModelFavoritesClient, ModelFavoritesConfig, ModelFavoritesErrorSink, ModelFavoritesState, ModelFetchScope, ModelFileHandle, ModelFit, ModelIdentityRow, ModelJson, ModelLabelSource, ModelNeed, ModelOfferingRow, ModelOption, ModelOptionWithLifecycle, ModelRecord, ModelRecordFetchType, ModelRecords, ModelRecordsClient, ModelRecordsConfig, ModelRecordsState, ModelSelectionPurpose, ModelSetEnv, ModelSetFacts, ModelSetValidation, NewAgentModel, NoSelection, OrderedLike, PreferredModelChoice, PreferredModelChoicePorts, PriceTier, PricedCatalogModel, PublicModelClient, RankOptions, RankedVariant, ReadModelCatalogOptions, SelectDevice, Selection, ShardName, SuccessionClient, SuccessionIndex, SuccessionNode, VariantDiscovery, VariantVerdict, Want };
|
|
2152
|
+
export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogKindVerdict, CatalogModel, CatalogTier, CheapestAdequateOptions, CompatStatus, ContentType, CuratedCatalogClient, CuratedModel, CuratedPresentation, CuratedRows, CuratedVariant, DecidedBy, DeviceAvailability, DeviceGraphics, DeviceHardware, DeviceModelFile, DeviceModelResolution, DeviceServedSettings, DiscoveryAmbiguity, DiscoveryArtifact, DiscoveryMedia, DiscoveryResult, DiscoveryRuntime, DiscoverySource, DiscoveryVariant, FeatureBucket, FeatureBucketMeta, FeatureKey, FitCapabilities, GgufHeader, GgufRead, GgufTensor, GroupedFeatures, HardwareClass, HardwareClasses, HeaderFlag, HeaderJudgement, HeaderRefusal, Interaction, InteractionMode, LocalArtifact, LocalCatalogClient, LocalCatalogQuery, LocalCompatRow, LocalModel, LocalModelCatalog, LocalPresentation, LocalRuntime, LocalRuntimeBuild, LocalVariant, Modality, ModelCapabilities, ModelCatalog, ModelCatalogChangeMeta, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogCoalesceOptions, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogListRows, ModelCatalogPgError, ModelCatalogPgResult, ModelCatalogQuery, ModelCatalogState, ModelCatalogVariant, ModelCatalogVariantState, ModelCatalogVisibilityOptions, ModelClassConfig, ModelClassEntry, ModelClassIndex, ModelDiagnosticEntry, ModelDiagnosticsSink, ModelFavorites, ModelFavoritesCache, ModelFavoritesClient, ModelFavoritesConfig, ModelFavoritesErrorSink, ModelFavoritesState, ModelFetchScope, ModelFileHandle, ModelFit, ModelIdentityRow, ModelJson, ModelLabelSource, ModelNeed, ModelOfferingRow, ModelOption, ModelOptionWithLifecycle, ModelRecord, ModelRecordFetchType, ModelRecords, ModelRecordsClient, ModelRecordsConfig, ModelRecordsState, ModelSelectionPurpose, ModelSetEnv, ModelSetFacts, ModelSetValidation, NewAgentModel, NoSelection, OrderedLike, PreferredModelChoice, PreferredModelChoicePorts, PriceTier, PricedCatalogModel, PublicModelClient, RankOptions, RankedVariant, ReadModelCatalogOptions, SelectDevice, Selection, ShardName, SuccessionClient, SuccessionIndex, SuccessionNode, VariantDiscovery, VariantVerdict, Want };
|
package/dist/models/index.d.ts
CHANGED
|
@@ -600,6 +600,15 @@ interface ModelRecord {
|
|
|
600
600
|
published_to_web_by: string | null;
|
|
601
601
|
}
|
|
602
602
|
type ModelIdentityRow = Pick<ModelRecord, "id" | "name" | "common_name" | "is_deprecated">;
|
|
603
|
+
/**
|
|
604
|
+
* What the picker list's read of the catalog returned, raw: every `ai.model_public` row (with at least the
|
|
605
|
+
* options columns) and every `ai.model_offering` row (`ModelOfferingRow` columns). `records.refresh(listed)`
|
|
606
|
+
* answers the options list, the identities and the class index from it instead of reading them again.
|
|
607
|
+
*/
|
|
608
|
+
interface ModelCatalogListRows {
|
|
609
|
+
publicRows: Array<Record<string, unknown>>;
|
|
610
|
+
offeringRows: ModelOfferingRow[];
|
|
611
|
+
}
|
|
603
612
|
type ModelClassConfig = {
|
|
604
613
|
modelId: string;
|
|
605
614
|
controls: ModelJson | null;
|
|
@@ -693,8 +702,14 @@ interface ModelRecords {
|
|
|
693
702
|
* the class index and slices, every pinned class's controls — and swap each in when it answers. The old
|
|
694
703
|
* rows stay on screen until then (no blank pill, no flicker); a failed re-read keeps them and reports
|
|
695
704
|
* through the error sink. Reads nothing that was never read. Never throws.
|
|
705
|
+
*
|
|
706
|
+
* Few requests, however many labels are mounted: full records are ONE `in (...)` read, identities are
|
|
707
|
+
* ONE `in (...)` read of only the ids no list row covers, model class slices are ONE read. `listed` is
|
|
708
|
+
* the picker list's own fresh re-read of `ai.model_public` + `ai.model_offering` (the catalog passes it,
|
|
709
|
+
* `createModelCatalog().refresh()`): when given, the options list, the identities it covers and the class
|
|
710
|
+
* index are answered from it, so one edit costs the page that one list read.
|
|
696
711
|
*/
|
|
697
|
-
refresh(): Promise<void>;
|
|
712
|
+
refresh(listed?: Promise<ModelCatalogListRows | null>): Promise<void>;
|
|
698
713
|
/**
|
|
699
714
|
* Answer `loadModel` + `loadModelClasses` for one model from rows a batched
|
|
700
715
|
* route read already returned (`chat.route_seed`): its `ai.model_config` row
|
|
@@ -2134,4 +2149,4 @@ interface ModelCatalogVisibilityOptions {
|
|
|
2134
2149
|
declare function followModelCatalogVisibility(options: ModelCatalogVisibilityOptions): () => void;
|
|
2135
2150
|
|
|
2136
2151
|
export { CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, DEFAULT_CAPABILITIES, DEVICE_FILE_USABLE_STATES, DEVICE_OWN_FILE_USABLE_STATES, DEVICE_RULE_TWIN_FIXTURE, DEVICE_SERVED_SETTINGS_KEY, DEVICE_SERVED_VIA, DeviceModelUnavailableError, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, GgufError, INPUT_MODALITIES, INTERACTION_MODES, LLM_INTERACTIONS, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, NEW_AGENT_DEFAULT_MODEL_KNOB, OUTPUT_MODALITIES, SERVICE_LABEL, SERVICE_LABEL_PLURAL, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, UNKNOWN_MODEL_KIND, acceptsTextInstructions, announceModelCatalogChanged, availableOnDevices, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, catalogKindVerdict, cheapestAdequateModel, classConfigKey, coalesceModelCatalogChanges, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, followModelCatalogChanges, followModelCatalogVisibility, formatMemoryBytes, fromDiscovery, ggufEngine, ggufFacts, groupFeatures, hardwareClassesOf, hasCompatibleDecisionInteraction, isChatModelCapability, isContentType, isConversationalModelCapability, isDecisionModelCapability, isFeatureKey, isHelperGguf, isInteractionMode, isListedVariant, isLocalModelCatalog, isManagedAgentModelCapability, isModelCatalogChange, isProjectorName, isSupersedable, isTopTierRating, judgeHeaderFacts, mergeAuditRecordIntoCapabilities, missingShards, modelCatalogRevision, modelClassKnobFor, modelClassName, modelClassPinOf, modelDisplayLabel, modelsForSelectionPurpose, normalizeArch, normalizeModel, normalizeModels, normalizeOs, normalizePromptSettings, onModelCatalogChanged, orderCatalogGroups, orderCatalogModels, outputFormatControlKey, parseCapabilities, pickProjector, priceTierRating, quantFromName, rankVariants, readCuratedRows, readDeviceModelFiles, readGgufHeader, readModelCatalog, readModelCatalogStamp, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifactIds, requiredArtifacts, resetSuccessionLogForTests, resolveDeviceModel, resolveLiveModelId, resolveModelMapKnob, resolveNewAgentModelId, resolvePreferredModelChoice, resolveSuccessor, runtimesOnEngine, seesImages, selectActiveModels, selectActiveModelsReady, selectAllModelOptions, selectAllModels, selectAllModelsReady, selectDeprecatedModels, selectDeprecatedModelsReady, selectModelById, selectModelClassConfig, selectModelClassName, selectModelControls, selectModelDetailError, selectModelFetchType, selectModelFullyLoaded, selectModelIdentityById, selectModelIdentityLookupStatus, selectModelLabelById, selectModelLabelWithClass, selectModelNameById, selectModelOptions, selectVariant, serviceCountLabel, setModelDiagnosticsSink, shapeCuratedModels, shardFileName, shardName, speedRatingLabel, speedRatingWord, splitSetProblems, takesInput, toAuditRecord, toDeviceModelFile, validateModelSet, variantVerdict, withModelClass };
|
|
2137
|
-
export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogKindVerdict, CatalogModel, CatalogTier, CheapestAdequateOptions, CompatStatus, ContentType, CuratedCatalogClient, CuratedModel, CuratedPresentation, CuratedRows, CuratedVariant, DecidedBy, DeviceAvailability, DeviceGraphics, DeviceHardware, DeviceModelFile, DeviceModelResolution, DeviceServedSettings, DiscoveryAmbiguity, DiscoveryArtifact, DiscoveryMedia, DiscoveryResult, DiscoveryRuntime, DiscoverySource, DiscoveryVariant, FeatureBucket, FeatureBucketMeta, FeatureKey, FitCapabilities, GgufHeader, GgufRead, GgufTensor, GroupedFeatures, HardwareClass, HardwareClasses, HeaderFlag, HeaderJudgement, HeaderRefusal, Interaction, InteractionMode, LocalArtifact, LocalCatalogClient, LocalCatalogQuery, LocalCompatRow, LocalModel, LocalModelCatalog, LocalPresentation, LocalRuntime, LocalRuntimeBuild, LocalVariant, Modality, ModelCapabilities, ModelCatalog, ModelCatalogChangeMeta, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogCoalesceOptions, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogPgError, ModelCatalogPgResult, ModelCatalogQuery, ModelCatalogState, ModelCatalogVariant, ModelCatalogVariantState, ModelCatalogVisibilityOptions, ModelClassConfig, ModelClassEntry, ModelClassIndex, ModelDiagnosticEntry, ModelDiagnosticsSink, ModelFavorites, ModelFavoritesCache, ModelFavoritesClient, ModelFavoritesConfig, ModelFavoritesErrorSink, ModelFavoritesState, ModelFetchScope, ModelFileHandle, ModelFit, ModelIdentityRow, ModelJson, ModelLabelSource, ModelNeed, ModelOfferingRow, ModelOption, ModelOptionWithLifecycle, ModelRecord, ModelRecordFetchType, ModelRecords, ModelRecordsClient, ModelRecordsConfig, ModelRecordsState, ModelSelectionPurpose, ModelSetEnv, ModelSetFacts, ModelSetValidation, NewAgentModel, NoSelection, OrderedLike, PreferredModelChoice, PreferredModelChoicePorts, PriceTier, PricedCatalogModel, PublicModelClient, RankOptions, RankedVariant, ReadModelCatalogOptions, SelectDevice, Selection, ShardName, SuccessionClient, SuccessionIndex, SuccessionNode, VariantDiscovery, VariantVerdict, Want };
|
|
2152
|
+
export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogKindVerdict, CatalogModel, CatalogTier, CheapestAdequateOptions, CompatStatus, ContentType, CuratedCatalogClient, CuratedModel, CuratedPresentation, CuratedRows, CuratedVariant, DecidedBy, DeviceAvailability, DeviceGraphics, DeviceHardware, DeviceModelFile, DeviceModelResolution, DeviceServedSettings, DiscoveryAmbiguity, DiscoveryArtifact, DiscoveryMedia, DiscoveryResult, DiscoveryRuntime, DiscoverySource, DiscoveryVariant, FeatureBucket, FeatureBucketMeta, FeatureKey, FitCapabilities, GgufHeader, GgufRead, GgufTensor, GroupedFeatures, HardwareClass, HardwareClasses, HeaderFlag, HeaderJudgement, HeaderRefusal, Interaction, InteractionMode, LocalArtifact, LocalCatalogClient, LocalCatalogQuery, LocalCompatRow, LocalModel, LocalModelCatalog, LocalPresentation, LocalRuntime, LocalRuntimeBuild, LocalVariant, Modality, ModelCapabilities, ModelCatalog, ModelCatalogChangeMeta, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogCoalesceOptions, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogListRows, ModelCatalogPgError, ModelCatalogPgResult, ModelCatalogQuery, ModelCatalogState, ModelCatalogVariant, ModelCatalogVariantState, ModelCatalogVisibilityOptions, ModelClassConfig, ModelClassEntry, ModelClassIndex, ModelDiagnosticEntry, ModelDiagnosticsSink, ModelFavorites, ModelFavoritesCache, ModelFavoritesClient, ModelFavoritesConfig, ModelFavoritesErrorSink, ModelFavoritesState, ModelFetchScope, ModelFileHandle, ModelFit, ModelIdentityRow, ModelJson, ModelLabelSource, ModelNeed, ModelOfferingRow, ModelOption, ModelOptionWithLifecycle, ModelRecord, ModelRecordFetchType, ModelRecords, ModelRecordsClient, ModelRecordsConfig, ModelRecordsState, ModelSelectionPurpose, ModelSetEnv, ModelSetFacts, ModelSetValidation, NewAgentModel, NoSelection, OrderedLike, PreferredModelChoice, PreferredModelChoicePorts, PriceTier, PricedCatalogModel, PublicModelClient, RankOptions, RankedVariant, ReadModelCatalogOptions, SelectDevice, Selection, ShardName, SuccessionClient, SuccessionIndex, SuccessionNode, VariantDiscovery, VariantVerdict, Want };
|
package/dist/models/index.js
CHANGED
|
@@ -837,6 +837,9 @@ function withIdLists(state2) {
|
|
|
837
837
|
const sort = (ids) => ids.sort((a, b) => label(a).localeCompare(label(b)));
|
|
838
838
|
return { ...state2, activeIds: sort(active), deprecatedIds: sort(deprecated) };
|
|
839
839
|
}
|
|
840
|
+
var IDENTITY_COLUMNS = "id, name, common_name, is_deprecated";
|
|
841
|
+
var OPTION_COLUMN_LIST = ["id", "name", "common_name", "maker", "cost_rating", "speed_rating", "is_primary", "capabilities", "is_deprecated", "retired_at", "successor_id"];
|
|
842
|
+
var OPTION_COLUMNS = OPTION_COLUMN_LIST.join(", ");
|
|
840
843
|
var MODEL_CONFIG_COLUMNS = "id, name, common_name, maker, cost_rating, speed_rating, is_premium, is_primary, context_window, max_tokens, capabilities, controls, constraints, is_deprecated, retired_at, successor_id";
|
|
841
844
|
function createModelRecords(config) {
|
|
842
845
|
if (!config?.client) throw new Error("createModelRecords: `client` (a Supabase client) is required.");
|
|
@@ -861,13 +864,10 @@ function createModelRecords(config) {
|
|
|
861
864
|
inFlight.set(key, p);
|
|
862
865
|
return p;
|
|
863
866
|
};
|
|
864
|
-
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
if (
|
|
868
|
-
const row = data;
|
|
869
|
-
if (!row) throw new Error(`AI model ${modelId} is not available (ai.${relation} has no such row)`);
|
|
870
|
-
if (!row.id || !row.name) throw new Error(`ai.${relation} returned a row without id/name for model ${modelId}`);
|
|
867
|
+
function toFullRecord(raw, relation, asked) {
|
|
868
|
+
const row = raw;
|
|
869
|
+
if (!row) throw new Error(`AI model ${asked} is not available (ai.${relation} has no such row)`);
|
|
870
|
+
if (!row.id || !row.name) throw new Error(`ai.${relation} returned a row without id/name for model ${asked}`);
|
|
871
871
|
const read = {
|
|
872
872
|
...row,
|
|
873
873
|
id: row.id,
|
|
@@ -877,74 +877,146 @@ function createModelRecords(config) {
|
|
|
877
877
|
};
|
|
878
878
|
return normalizeModel(read);
|
|
879
879
|
}
|
|
880
|
-
async function
|
|
880
|
+
async function readFullModel(modelId) {
|
|
881
|
+
const relation = await isSignedIn() ? "model_config" : "model_public";
|
|
882
|
+
const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
|
|
883
|
+
if (error) throw error;
|
|
884
|
+
return toFullRecord(data, relation, modelId);
|
|
885
|
+
}
|
|
886
|
+
async function readOfferings(modelIds) {
|
|
881
887
|
const q = ai().from("model_offering").select("model_id, offering_id, served_via, served_via_endpoint_id, priority");
|
|
882
|
-
const { data, error } = await (
|
|
888
|
+
const { data, error } = await (modelIds ? modelIds.length === 1 ? q.eq("model_id", modelIds[0]) : q.in("model_id", modelIds) : q);
|
|
883
889
|
if (error) throw error;
|
|
884
890
|
return data ?? [];
|
|
885
891
|
}
|
|
886
|
-
|
|
892
|
+
const mergeClassSlice = (rows3) => update((s4) => {
|
|
893
|
+
if (s4.modelClassStatus === "succeeded") return s4;
|
|
894
|
+
const slice = buildModelClassIndex(rows3);
|
|
895
|
+
const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
|
|
896
|
+
return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
|
|
897
|
+
});
|
|
898
|
+
let identityTick = null;
|
|
899
|
+
async function readIdentitiesNow(wanted) {
|
|
900
|
+
try {
|
|
901
|
+
const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).in("id", wanted);
|
|
902
|
+
if (error) throw error;
|
|
903
|
+
const rows3 = Array.isArray(data) ? data : [];
|
|
904
|
+
const got = new Set(rows3.map((r) => r.id));
|
|
905
|
+
const missing = wanted.filter((id) => !got.has(id));
|
|
906
|
+
if (missing.length > 0) errorSink({ code: "model-records-identity-failed", message: `AI model ${missing.join(", ")} is not available (ai.model_definition has no such row)`, context: { modelIds: missing } });
|
|
907
|
+
update((s4) => ({
|
|
908
|
+
...s4,
|
|
909
|
+
identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
|
|
910
|
+
identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"])) }
|
|
911
|
+
}));
|
|
912
|
+
} catch (err) {
|
|
913
|
+
errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
|
|
914
|
+
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) } }));
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
function queueIdentity(id) {
|
|
918
|
+
if (!identityTick) {
|
|
919
|
+
const ids = /* @__PURE__ */ new Set();
|
|
920
|
+
const done = Promise.resolve().then(() => {
|
|
921
|
+
identityTick = null;
|
|
922
|
+
return readIdentitiesNow([...ids]);
|
|
923
|
+
});
|
|
924
|
+
identityTick = { ids, done };
|
|
925
|
+
void done.finally(() => {
|
|
926
|
+
for (const queued of ids) if (inFlight.get(`identity:${queued}`) === done) inFlight.delete(`identity:${queued}`);
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
identityTick.ids.add(id);
|
|
930
|
+
inFlight.set(`identity:${id}`, identityTick.done);
|
|
931
|
+
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [id]: "loading" } }));
|
|
932
|
+
return identityTick.done;
|
|
933
|
+
}
|
|
934
|
+
async function rereadKnown(listed) {
|
|
887
935
|
const known = state2;
|
|
888
936
|
const jobs = [];
|
|
889
937
|
const fail = (code, message, context) => errorSink({ code, message: `Catalog refresh: ${message}`, ...context ? { context } : {} });
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
938
|
+
const fed = listed ? listed.then((f) => f, () => null) : Promise.resolve(null);
|
|
939
|
+
const optionsWereRead = known.fetchScope === "active" || known.fetchScope === "all";
|
|
940
|
+
const freshOptions = optionsWereRead ? fed.then((f) => f ? optionRowsOf(f.publicRows, f.offeringRows) : readOptions()) : Promise.resolve(null);
|
|
941
|
+
jobs.push(
|
|
942
|
+
freshOptions.then(
|
|
943
|
+
(rows3) => {
|
|
944
|
+
if (!rows3) return;
|
|
945
|
+
update((s4) => {
|
|
894
946
|
const entities = { ...s4.entities };
|
|
895
947
|
for (const row of rows3) {
|
|
896
948
|
const existing = entities[row.id];
|
|
897
949
|
entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: existing?._fetchType === "full" ? "full" : "options" };
|
|
898
950
|
}
|
|
899
951
|
return withIdLists({ ...s4, entities, lastFetched: Date.now(), error: null });
|
|
900
|
-
})
|
|
901
|
-
|
|
902
|
-
)
|
|
903
|
-
)
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
952
|
+
});
|
|
953
|
+
},
|
|
954
|
+
(err) => fail("model-records-refresh-options-failed", messageOf(err))
|
|
955
|
+
)
|
|
956
|
+
);
|
|
957
|
+
const fullIds = Object.entries(known.entities).filter(([, r]) => r._fetchType === "full").map(([id]) => id);
|
|
958
|
+
if (fullIds.length > 0) {
|
|
907
959
|
jobs.push(
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
960
|
+
(async () => {
|
|
961
|
+
const relation = await isSignedIn() ? "model_config" : "model_public";
|
|
962
|
+
const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).in("id", fullIds);
|
|
963
|
+
if (error) throw error;
|
|
964
|
+
const byId2 = new Map((Array.isArray(data) ? data : []).map((r) => [r.id, r]));
|
|
965
|
+
for (const id of fullIds) {
|
|
966
|
+
try {
|
|
967
|
+
const fresh = toFullRecord(byId2.get(id) ?? null, relation, id);
|
|
968
|
+
update((s4) => {
|
|
969
|
+
const existing = s4.entities[fresh.id];
|
|
970
|
+
return withIdLists({ ...s4, entities: { ...s4.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
|
|
971
|
+
});
|
|
972
|
+
} catch (err) {
|
|
973
|
+
fail("model-records-refresh-model-failed", `${id}: ${messageOf(err)}`, { modelId: id });
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
})().catch((err) => fail("model-records-refresh-model-failed", `${fullIds.join(", ")}: ${messageOf(err)}`, { modelIds: fullIds }))
|
|
915
977
|
);
|
|
916
978
|
}
|
|
917
|
-
|
|
979
|
+
const identityKeys = Object.entries(known.identityById);
|
|
980
|
+
if (identityKeys.length > 0) {
|
|
918
981
|
jobs.push(
|
|
919
|
-
|
|
920
|
-
|
|
982
|
+
(async () => {
|
|
983
|
+
const [f, opts] = await Promise.all([fed, freshOptions.catch(() => null)]);
|
|
984
|
+
const covered = /* @__PURE__ */ new Map();
|
|
985
|
+
for (const r of [...f?.publicRows ?? [], ...opts ?? []]) {
|
|
986
|
+
if (typeof r.id === "string" && typeof r.name === "string") {
|
|
987
|
+
covered.set(r.id, { id: r.id, name: r.name, common_name: r.common_name ?? null, is_deprecated: r.is_deprecated ?? null });
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
const wanted = [...new Set(identityKeys.map(([, row]) => row.id))].filter((id) => !covered.has(id) && known.entities[id]?._fetchType !== "full");
|
|
991
|
+
if (wanted.length > 0) {
|
|
992
|
+
const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).in("id", wanted);
|
|
921
993
|
if (error) throw error;
|
|
922
|
-
|
|
994
|
+
for (const row of Array.isArray(data) ? data : []) covered.set(row.id, row);
|
|
995
|
+
}
|
|
996
|
+
const next = {};
|
|
997
|
+
for (const [key, row] of identityKeys) {
|
|
998
|
+
const fresh = covered.get(row.id);
|
|
999
|
+
if (fresh) next[key] = fresh;
|
|
923
1000
|
}
|
|
924
|
-
|
|
1001
|
+
if (Object.keys(next).length > 0) update((s4) => ({ ...s4, identityById: { ...s4.identityById, ...next } }));
|
|
1002
|
+
})().catch((err) => fail("model-records-refresh-identity-failed", messageOf(err)))
|
|
925
1003
|
);
|
|
926
1004
|
}
|
|
927
1005
|
if (known.modelClassStatus === "succeeded") {
|
|
928
1006
|
jobs.push(
|
|
929
|
-
readOfferings().then(
|
|
1007
|
+
fed.then((f) => f?.offeringRows ?? readOfferings()).then(
|
|
930
1008
|
(rows3) => update((s4) => ({ ...s4, modelClassIndex: buildModelClassIndex(rows3) })),
|
|
931
1009
|
(err) => fail("model-records-refresh-classes-failed", messageOf(err))
|
|
932
1010
|
)
|
|
933
1011
|
);
|
|
934
|
-
} else {
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
|
|
943
|
-
}),
|
|
944
|
-
(err) => fail("model-records-refresh-classes-failed", `${id}: ${messageOf(err)}`, { modelId: id })
|
|
945
|
-
)
|
|
946
|
-
);
|
|
947
|
-
}
|
|
1012
|
+
} else if (classesReadFor.size > 0) {
|
|
1013
|
+
const ids = [...classesReadFor];
|
|
1014
|
+
jobs.push(
|
|
1015
|
+
fed.then((f) => f ? f.offeringRows.filter((o) => o.model_id !== null && classesReadFor.has(o.model_id)) : readOfferings(ids)).then(
|
|
1016
|
+
(rows3) => mergeClassSlice(rows3),
|
|
1017
|
+
(err) => fail("model-records-refresh-classes-failed", `${ids.join(", ")}: ${messageOf(err)}`, { modelIds: ids })
|
|
1018
|
+
)
|
|
1019
|
+
);
|
|
948
1020
|
}
|
|
949
1021
|
for (const [key, cfg] of Object.entries(known.classConfigByOffering)) {
|
|
950
1022
|
const offeringId = key.slice(cfg.modelId.length + 1);
|
|
@@ -962,16 +1034,29 @@ function createModelRecords(config) {
|
|
|
962
1034
|
}
|
|
963
1035
|
let refreshing = null;
|
|
964
1036
|
let refreshAgain = null;
|
|
965
|
-
function refreshAll() {
|
|
1037
|
+
function refreshAll(listed) {
|
|
966
1038
|
if (refreshing) return refreshAgain ??= refreshing.then(() => {
|
|
967
1039
|
refreshAgain = null;
|
|
968
1040
|
return refreshAll();
|
|
969
1041
|
});
|
|
970
|
-
refreshing = rereadKnown().finally(() => {
|
|
1042
|
+
refreshing = rereadKnown(listed).finally(() => {
|
|
971
1043
|
refreshing = null;
|
|
972
1044
|
});
|
|
973
1045
|
return refreshing;
|
|
974
1046
|
}
|
|
1047
|
+
function optionRowsOf(publicRows, offeringRows) {
|
|
1048
|
+
const routable = new Set(offeringRows.map((r) => r.model_id).filter((id) => typeof id === "string" && id.length > 0));
|
|
1049
|
+
return publicRows.filter((r) => typeof r.id === "string" && typeof r.name === "string" && routable.has(r.id)).map((r) => {
|
|
1050
|
+
const picked = {};
|
|
1051
|
+
for (const c of OPTION_COLUMN_LIST) if (c in r) picked[c] = r[c];
|
|
1052
|
+
return {
|
|
1053
|
+
...picked,
|
|
1054
|
+
id: r.id,
|
|
1055
|
+
name: r.name,
|
|
1056
|
+
capabilities: requireCanonicalCapabilities(r.capabilities, { modelId: r.id, modelName: r.name })
|
|
1057
|
+
};
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
975
1060
|
async function readOptions() {
|
|
976
1061
|
const { data: offeringRows, error: offeringError } = await ai().from("model_offering").select("model_id");
|
|
977
1062
|
if (offeringError) throw offeringError;
|
|
@@ -981,12 +1066,9 @@ function createModelRecords(config) {
|
|
|
981
1066
|
)
|
|
982
1067
|
];
|
|
983
1068
|
if (routableIds.length === 0) return [];
|
|
984
|
-
const { data, error } = await ai().from("model_public").select(
|
|
1069
|
+
const { data, error } = await ai().from("model_public").select(OPTION_COLUMNS).in("id", routableIds).order("common_name", { ascending: true, nullsFirst: false });
|
|
985
1070
|
if (error) throw error;
|
|
986
|
-
return (data ?? []
|
|
987
|
-
...r,
|
|
988
|
-
capabilities: requireCanonicalCapabilities(r.capabilities, { modelId: r.id, modelName: r.name })
|
|
989
|
-
}));
|
|
1071
|
+
return optionRowsOf(data ?? [], routableIds.map((model_id) => ({ model_id })));
|
|
990
1072
|
}
|
|
991
1073
|
const api = {
|
|
992
1074
|
getState: () => state2,
|
|
@@ -1143,10 +1225,11 @@ function createModelRecords(config) {
|
|
|
1143
1225
|
if (!modelId || state2.entities[modelId] || state2.identityById[modelId] || status === "loading" || status === "succeeded" || status === "failed") {
|
|
1144
1226
|
return inFlight.get(`identity:${modelId}`) ?? Promise.resolve();
|
|
1145
1227
|
}
|
|
1228
|
+
if (catalogKeyColumn(modelId) === "id") return queueIdentity(modelId);
|
|
1146
1229
|
return once(`identity:${modelId}`, async () => {
|
|
1147
1230
|
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "loading" } }));
|
|
1148
1231
|
try {
|
|
1149
|
-
const { data, error } = await ai().from("model_definition").select(
|
|
1232
|
+
const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
|
|
1150
1233
|
if (error) throw error;
|
|
1151
1234
|
if (!data) throw new Error(`AI model ${modelId} is not available (ai.model_definition has no such row)`);
|
|
1152
1235
|
const row = data;
|
|
@@ -1162,52 +1245,10 @@ function createModelRecords(config) {
|
|
|
1162
1245
|
});
|
|
1163
1246
|
},
|
|
1164
1247
|
loadIdentities(modelIds) {
|
|
1165
|
-
const asked = (id) => {
|
|
1166
|
-
const status = state2.identityStatusById[id];
|
|
1167
|
-
return Boolean(state2.entities[id] || state2.identityById[id] || status === "loading" || status === "succeeded" || status === "failed");
|
|
1168
|
-
};
|
|
1169
1248
|
const ids = [...new Set(modelIds.filter((id) => Boolean(id)))];
|
|
1170
|
-
|
|
1171
|
-
const wanted = [];
|
|
1172
|
-
for (const id of ids) {
|
|
1173
|
-
if (catalogKeyColumn(id) !== "id") waits.push(api.loadIdentity(id));
|
|
1174
|
-
else if (asked(id)) waits.push(inFlight.get(`identity:${id}`) ?? Promise.resolve());
|
|
1175
|
-
else wanted.push(id);
|
|
1176
|
-
}
|
|
1177
|
-
if (wanted.length > 0) {
|
|
1178
|
-
const marked = Object.fromEntries(wanted.map((id) => [id, "loading"]));
|
|
1179
|
-
update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...marked } }));
|
|
1180
|
-
const run = (async () => {
|
|
1181
|
-
try {
|
|
1182
|
-
const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").in("id", wanted);
|
|
1183
|
-
if (error) throw error;
|
|
1184
|
-
const rows3 = Array.isArray(data) ? data : [];
|
|
1185
|
-
const got = new Set(rows3.map((r) => r.id));
|
|
1186
|
-
update((s4) => ({
|
|
1187
|
-
...s4,
|
|
1188
|
-
identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
|
|
1189
|
-
identityStatusById: {
|
|
1190
|
-
...s4.identityStatusById,
|
|
1191
|
-
...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"]))
|
|
1192
|
-
}
|
|
1193
|
-
}));
|
|
1194
|
-
} catch (err) {
|
|
1195
|
-
errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
|
|
1196
|
-
update((s4) => ({
|
|
1197
|
-
...s4,
|
|
1198
|
-
identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) }
|
|
1199
|
-
}));
|
|
1200
|
-
}
|
|
1201
|
-
})();
|
|
1202
|
-
for (const id of wanted) inFlight.set(`identity:${id}`, run);
|
|
1203
|
-
void run.finally(() => {
|
|
1204
|
-
for (const id of wanted) if (inFlight.get(`identity:${id}`) === run) inFlight.delete(`identity:${id}`);
|
|
1205
|
-
});
|
|
1206
|
-
waits.push(run);
|
|
1207
|
-
}
|
|
1208
|
-
return Promise.all(waits).then(() => void 0);
|
|
1249
|
+
return Promise.all(ids.map((id) => api.loadIdentity(id))).then(() => void 0);
|
|
1209
1250
|
},
|
|
1210
|
-
refresh: () => refreshAll(),
|
|
1251
|
+
refresh: (listed) => refreshAll(listed),
|
|
1211
1252
|
seedModel({ modelId, config: config2, offerings }) {
|
|
1212
1253
|
if (!modelId) return;
|
|
1213
1254
|
if (!classesReadFor.has(modelId) && state2.modelClassStatus !== "succeeded") {
|
|
@@ -1656,7 +1697,7 @@ function aiRpc(client, fn) {
|
|
|
1656
1697
|
if (!scope.rpc) throw new Error(`The model catalog client's schema("ai") has no rpc(), so ${fn} cannot be called.`);
|
|
1657
1698
|
return scope.rpc(fn);
|
|
1658
1699
|
}
|
|
1659
|
-
async function readCatalog(client, variant) {
|
|
1700
|
+
async function readCatalog(client, variant, onListRows) {
|
|
1660
1701
|
if (variant === "admin") {
|
|
1661
1702
|
const [rpcRes, offeringsRes] = await Promise.all([
|
|
1662
1703
|
aiRpc(client, "admin_model_catalog"),
|
|
@@ -1707,6 +1748,7 @@ async function readCatalog(client, variant) {
|
|
|
1707
1748
|
]);
|
|
1708
1749
|
if (publicRes.error) throw publicRes.error;
|
|
1709
1750
|
if (offeringRes.error) throw offeringRes.error;
|
|
1751
|
+
onListRows?.({ publicRows: rows(publicRes.data), offeringRows: rows(offeringRes.data) });
|
|
1710
1752
|
const tiersByModel = /* @__PURE__ */ new Map();
|
|
1711
1753
|
for (const r of rows(offeringRes.data)) {
|
|
1712
1754
|
const modelId = str(r.model_id);
|
|
@@ -1746,14 +1788,14 @@ function createModelCatalog(config) {
|
|
|
1746
1788
|
state2 = { ...state2, [variant]: next };
|
|
1747
1789
|
for (const l of [...listeners]) l();
|
|
1748
1790
|
};
|
|
1749
|
-
const run = (variant) => {
|
|
1791
|
+
const run = (variant, onListRows) => {
|
|
1750
1792
|
const pending = inFlight[variant];
|
|
1751
1793
|
if (pending) return pending;
|
|
1752
1794
|
const prior = state2[variant];
|
|
1753
1795
|
if (prior.status === "idle" || prior.status === "error") set(variant, { ...prior, status: "loading", error: null });
|
|
1754
1796
|
const job = (async () => {
|
|
1755
1797
|
try {
|
|
1756
|
-
const models = await readCatalog(client, variant);
|
|
1798
|
+
const models = await readCatalog(client, variant, onListRows);
|
|
1757
1799
|
set(variant, { status: "ready", models, error: null, loadedAt: Date.now() });
|
|
1758
1800
|
} catch (err) {
|
|
1759
1801
|
const message = messageOf2(err);
|
|
@@ -1773,7 +1815,15 @@ function createModelCatalog(config) {
|
|
|
1773
1815
|
});
|
|
1774
1816
|
const refresh = async () => {
|
|
1775
1817
|
const variants = Object.keys(state2).filter((v) => state2[v].status !== "idle");
|
|
1776
|
-
|
|
1818
|
+
let listed;
|
|
1819
|
+
const runs = variants.map((v) => {
|
|
1820
|
+
if (v !== "user" || inFlight.user) return run(v);
|
|
1821
|
+
let give = () => {
|
|
1822
|
+
};
|
|
1823
|
+
listed = new Promise((resolve) => give = resolve);
|
|
1824
|
+
return run("user", (rows3) => give(rows3)).finally(() => give(null));
|
|
1825
|
+
});
|
|
1826
|
+
await Promise.all([records.refresh(listed), ...runs]);
|
|
1777
1827
|
};
|
|
1778
1828
|
const stopFollowing = config.followChanges === false ? () => {
|
|
1779
1829
|
} : onModelCatalogChanged(() => void refresh());
|
|
@@ -288,6 +288,15 @@ interface ModelRecord {
|
|
|
288
288
|
published_to_web_by: string | null;
|
|
289
289
|
}
|
|
290
290
|
type ModelIdentityRow = Pick<ModelRecord, "id" | "name" | "common_name" | "is_deprecated">;
|
|
291
|
+
/**
|
|
292
|
+
* What the picker list's read of the catalog returned, raw: every `ai.model_public` row (with at least the
|
|
293
|
+
* options columns) and every `ai.model_offering` row (`ModelOfferingRow` columns). `records.refresh(listed)`
|
|
294
|
+
* answers the options list, the identities and the class index from it instead of reading them again.
|
|
295
|
+
*/
|
|
296
|
+
interface ModelCatalogListRows {
|
|
297
|
+
publicRows: Array<Record<string, unknown>>;
|
|
298
|
+
offeringRows: ModelOfferingRow[];
|
|
299
|
+
}
|
|
291
300
|
type ModelClassConfig = {
|
|
292
301
|
modelId: string;
|
|
293
302
|
controls: ModelJson | null;
|
|
@@ -343,8 +352,14 @@ interface ModelRecords {
|
|
|
343
352
|
* the class index and slices, every pinned class's controls — and swap each in when it answers. The old
|
|
344
353
|
* rows stay on screen until then (no blank pill, no flicker); a failed re-read keeps them and reports
|
|
345
354
|
* through the error sink. Reads nothing that was never read. Never throws.
|
|
355
|
+
*
|
|
356
|
+
* Few requests, however many labels are mounted: full records are ONE `in (...)` read, identities are
|
|
357
|
+
* ONE `in (...)` read of only the ids no list row covers, model class slices are ONE read. `listed` is
|
|
358
|
+
* the picker list's own fresh re-read of `ai.model_public` + `ai.model_offering` (the catalog passes it,
|
|
359
|
+
* `createModelCatalog().refresh()`): when given, the options list, the identities it covers and the class
|
|
360
|
+
* index are answered from it, so one edit costs the page that one list read.
|
|
346
361
|
*/
|
|
347
|
-
refresh(): Promise<void>;
|
|
362
|
+
refresh(listed?: Promise<ModelCatalogListRows | null>): Promise<void>;
|
|
348
363
|
/**
|
|
349
364
|
* Answer `loadModel` + `loadModelClasses` for one model from rows a batched
|
|
350
365
|
* route read already returned (`chat.route_seed`): its `ai.model_config` row
|
|
@@ -288,6 +288,15 @@ interface ModelRecord {
|
|
|
288
288
|
published_to_web_by: string | null;
|
|
289
289
|
}
|
|
290
290
|
type ModelIdentityRow = Pick<ModelRecord, "id" | "name" | "common_name" | "is_deprecated">;
|
|
291
|
+
/**
|
|
292
|
+
* What the picker list's read of the catalog returned, raw: every `ai.model_public` row (with at least the
|
|
293
|
+
* options columns) and every `ai.model_offering` row (`ModelOfferingRow` columns). `records.refresh(listed)`
|
|
294
|
+
* answers the options list, the identities and the class index from it instead of reading them again.
|
|
295
|
+
*/
|
|
296
|
+
interface ModelCatalogListRows {
|
|
297
|
+
publicRows: Array<Record<string, unknown>>;
|
|
298
|
+
offeringRows: ModelOfferingRow[];
|
|
299
|
+
}
|
|
291
300
|
type ModelClassConfig = {
|
|
292
301
|
modelId: string;
|
|
293
302
|
controls: ModelJson | null;
|
|
@@ -343,8 +352,14 @@ interface ModelRecords {
|
|
|
343
352
|
* the class index and slices, every pinned class's controls — and swap each in when it answers. The old
|
|
344
353
|
* rows stay on screen until then (no blank pill, no flicker); a failed re-read keeps them and reports
|
|
345
354
|
* through the error sink. Reads nothing that was never read. Never throws.
|
|
355
|
+
*
|
|
356
|
+
* Few requests, however many labels are mounted: full records are ONE `in (...)` read, identities are
|
|
357
|
+
* ONE `in (...)` read of only the ids no list row covers, model class slices are ONE read. `listed` is
|
|
358
|
+
* the picker list's own fresh re-read of `ai.model_public` + `ai.model_offering` (the catalog passes it,
|
|
359
|
+
* `createModelCatalog().refresh()`): when given, the options list, the identities it covers and the class
|
|
360
|
+
* index are answered from it, so one edit costs the page that one list read.
|
|
346
361
|
*/
|
|
347
|
-
refresh(): Promise<void>;
|
|
362
|
+
refresh(listed?: Promise<ModelCatalogListRows | null>): Promise<void>;
|
|
348
363
|
/**
|
|
349
364
|
* Answer `loadModel` + `loadModelClasses` for one model from rows a batched
|
|
350
365
|
* route read already returned (`chat.route_seed`): its `ai.model_config` row
|
|
@@ -1107,6 +1107,7 @@ var import_design_system10 = require("@ai-matrx/design-system");
|
|
|
1107
1107
|
var import_data_table = require("@ai-matrx/design-system/data-table");
|
|
1108
1108
|
var import_design_system11 = require("@ai-matrx/design-system");
|
|
1109
1109
|
var import_text_case4 = require("@ai-matrx/kit/text-case");
|
|
1110
|
+
var import_error_sentence = require("@ai-matrx/design-system/error-sentence");
|
|
1110
1111
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1111
1112
|
var SILENT = { success: () => {
|
|
1112
1113
|
}, error: () => {
|
|
@@ -1275,7 +1276,7 @@ function ContractBreak({ version, ports }) {
|
|
|
1275
1276
|
await ports.onDeclare(version.version_number, kind);
|
|
1276
1277
|
} catch (error) {
|
|
1277
1278
|
setDeclared(previous);
|
|
1278
|
-
ports.notify.error(
|
|
1279
|
+
ports.notify.error("Break not recorded", { description: (0, import_error_sentence.peopleErrorSentence)(error, null, { card: true }) });
|
|
1279
1280
|
return;
|
|
1280
1281
|
}
|
|
1281
1282
|
{
|
|
@@ -97,7 +97,10 @@ interface VersionHistoryLoad {
|
|
|
97
97
|
/** Where the timeline says a result: the host's toasts (sonner on the web). */
|
|
98
98
|
interface VersionTimelineNotify {
|
|
99
99
|
success: (message: string) => void;
|
|
100
|
-
|
|
100
|
+
/** A card: a short title and an optional line (design-system NOTICE_TEXT_BUDGET). */
|
|
101
|
+
error: (message: string, options?: {
|
|
102
|
+
description?: string;
|
|
103
|
+
}) => void;
|
|
101
104
|
}
|
|
102
105
|
interface VersionHistoryTimelineProps {
|
|
103
106
|
agentId: string;
|
|
@@ -97,7 +97,10 @@ interface VersionHistoryLoad {
|
|
|
97
97
|
/** Where the timeline says a result: the host's toasts (sonner on the web). */
|
|
98
98
|
interface VersionTimelineNotify {
|
|
99
99
|
success: (message: string) => void;
|
|
100
|
-
|
|
100
|
+
/** A card: a short title and an optional line (design-system NOTICE_TEXT_BUDGET). */
|
|
101
|
+
error: (message: string, options?: {
|
|
102
|
+
description?: string;
|
|
103
|
+
}) => void;
|
|
101
104
|
}
|
|
102
105
|
interface VersionHistoryTimelineProps {
|
|
103
106
|
agentId: string;
|
|
@@ -1111,6 +1111,7 @@ import {
|
|
|
1111
1111
|
} from "@ai-matrx/design-system/data-table";
|
|
1112
1112
|
import { RecordId } from "@ai-matrx/design-system";
|
|
1113
1113
|
import { humanizeIdentifier as humanizeIdentifier4 } from "@ai-matrx/kit/text-case";
|
|
1114
|
+
import { peopleErrorSentence } from "@ai-matrx/design-system/error-sentence";
|
|
1114
1115
|
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1115
1116
|
var SILENT = { success: () => {
|
|
1116
1117
|
}, error: () => {
|
|
@@ -1279,7 +1280,7 @@ function ContractBreak({ version, ports }) {
|
|
|
1279
1280
|
await ports.onDeclare(version.version_number, kind);
|
|
1280
1281
|
} catch (error) {
|
|
1281
1282
|
setDeclared(previous);
|
|
1282
|
-
ports.notify.error(
|
|
1283
|
+
ports.notify.error("Break not recorded", { description: peopleErrorSentence(error, null, { card: true }) });
|
|
1283
1284
|
return;
|
|
1284
1285
|
}
|
|
1285
1286
|
{
|
|
@@ -947,6 +947,18 @@
|
|
|
947
947
|
],
|
|
948
948
|
"default": null
|
|
949
949
|
},
|
|
950
|
+
"conversation_instructions": {
|
|
951
|
+
"anyOf": [
|
|
952
|
+
{
|
|
953
|
+
"type": "string"
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
"type": "null"
|
|
957
|
+
}
|
|
958
|
+
],
|
|
959
|
+
"default": null,
|
|
960
|
+
"title": "Conversation Instructions"
|
|
961
|
+
},
|
|
950
962
|
"max_output_tokens": {
|
|
951
963
|
"anyOf": [
|
|
952
964
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/agents",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.277",
|
|
4
4
|
"description": "Portable AI Matrx agent stream protocol, event projection, and safe result-presentation primitives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -522,5 +522,5 @@
|
|
|
522
522
|
"optional": true
|
|
523
523
|
}
|
|
524
524
|
},
|
|
525
|
-
"gitHead": "
|
|
525
|
+
"gitHead": "9798073998e5fa0c03b673a38fda129a2bc7d19d"
|
|
526
526
|
}
|