@ai-matrx/agents 0.59.257 → 0.59.258
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/models/index.cjs +74 -4
- package/dist/models/index.d.cts +91 -1
- package/dist/models/index.d.ts +91 -1
- package/dist/models/index.js +74 -4
- package/dist/models/react/index.cjs +31 -1
- package/dist/models/react/index.d.cts +31 -1
- package/dist/models/react/index.d.ts +31 -1
- package/dist/models/react/index.js +37 -7
- package/package.json +6 -5
- package/scripts/check-model-catalog-reads.mjs +122 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.59.
|
|
3
|
+
## 0.59.258
|
|
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.257
|
|
9
|
+
|
|
10
|
+
Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff 76b41cb76b 0a2b8c9356 -- apps/shared/matrx-agents/src`
|
|
11
|
+
has no shipped-source changes outside tests, fixtures and docs). No consumer action required.
|
|
12
|
+
|
|
8
13
|
## 0.59.256
|
|
9
14
|
|
|
10
15
|
Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff be3d056be5 76b41cb76b -- apps/shared/matrx-agents/src`
|
package/dist/models/index.cjs
CHANGED
|
@@ -42,6 +42,7 @@ __export(models_exports, {
|
|
|
42
42
|
TOP_TIER_COST_RATING: () => TOP_TIER_COST_RATING,
|
|
43
43
|
TOP_TIER_LOCKED_TITLE: () => TOP_TIER_LOCKED_TITLE,
|
|
44
44
|
acceptsTextInstructions: () => acceptsTextInstructions,
|
|
45
|
+
announceModelCatalogChanged: () => announceModelCatalogChanged,
|
|
45
46
|
blendedPrice: () => blendedPrice,
|
|
46
47
|
buildModelClassIndex: () => buildModelClassIndex,
|
|
47
48
|
buildSuccessionIndex: () => buildSuccessionIndex,
|
|
@@ -65,6 +66,7 @@ __export(models_exports, {
|
|
|
65
66
|
fitsMemory: () => fitsMemory,
|
|
66
67
|
fitsNeed: () => fitsNeed,
|
|
67
68
|
fitsThisComputer: () => fitsThisComputer,
|
|
69
|
+
followModelCatalogChanges: () => followModelCatalogChanges,
|
|
68
70
|
formatMemoryBytes: () => formatMemoryBytes,
|
|
69
71
|
fromDiscovery: () => fromDiscovery,
|
|
70
72
|
ggufEngine: () => ggufEngine,
|
|
@@ -82,21 +84,25 @@ __export(models_exports, {
|
|
|
82
84
|
isListedVariant: () => isListedVariant,
|
|
83
85
|
isLocalModelCatalog: () => isLocalModelCatalog,
|
|
84
86
|
isManagedAgentModelCapability: () => isManagedAgentModelCapability,
|
|
87
|
+
isModelCatalogChange: () => isModelCatalogChange,
|
|
85
88
|
isProjectorName: () => isProjectorName,
|
|
86
89
|
isSupersedable: () => isSupersedable,
|
|
87
90
|
isTopTierRating: () => isTopTierRating,
|
|
88
91
|
judgeHeaderFacts: () => judgeHeaderFacts,
|
|
89
92
|
mergeAuditRecordIntoCapabilities: () => mergeAuditRecordIntoCapabilities,
|
|
90
93
|
missingShards: () => missingShards,
|
|
94
|
+
modelCatalogRevision: () => modelCatalogRevision,
|
|
91
95
|
modelClassKnobFor: () => modelClassKnobFor,
|
|
92
96
|
modelClassName: () => modelClassName,
|
|
93
97
|
modelClassPinOf: () => modelClassPinOf,
|
|
98
|
+
modelDisplayLabel: () => modelDisplayLabel,
|
|
94
99
|
modelsForSelectionPurpose: () => modelsForSelectionPurpose,
|
|
95
100
|
normalizeArch: () => normalizeArch,
|
|
96
101
|
normalizeModel: () => normalizeModel,
|
|
97
102
|
normalizeModels: () => normalizeModels,
|
|
98
103
|
normalizeOs: () => normalizeOs,
|
|
99
104
|
normalizePromptSettings: () => normalizePromptSettings,
|
|
105
|
+
onModelCatalogChanged: () => onModelCatalogChanged,
|
|
100
106
|
orderCatalogGroups: () => orderCatalogGroups,
|
|
101
107
|
orderCatalogModels: () => orderCatalogModels,
|
|
102
108
|
outputFormatControlKey: () => outputFormatControlKey,
|
|
@@ -108,6 +114,8 @@ __export(models_exports, {
|
|
|
108
114
|
readCuratedRows: () => readCuratedRows,
|
|
109
115
|
readGgufHeader: () => readGgufHeader,
|
|
110
116
|
readModelCatalog: () => readModelCatalog,
|
|
117
|
+
readModelIdentities: () => readModelIdentities,
|
|
118
|
+
readPublicModel: () => readPublicModel,
|
|
111
119
|
reconcileModelFavoriteIds: () => reconcileModelFavoriteIds,
|
|
112
120
|
reportModelDiagnostic: () => reportModelDiagnostic,
|
|
113
121
|
requireCanonicalCapabilities: () => requireCanonicalCapabilities,
|
|
@@ -918,6 +926,48 @@ var OUTPUT_MODALITIES = [
|
|
|
918
926
|
"decision"
|
|
919
927
|
];
|
|
920
928
|
|
|
929
|
+
// models/catalog/changes.ts
|
|
930
|
+
var KEY2 = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
|
|
931
|
+
function hub() {
|
|
932
|
+
const g = globalThis;
|
|
933
|
+
return g[KEY2] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
|
|
934
|
+
}
|
|
935
|
+
function isModelCatalogChange(payload) {
|
|
936
|
+
const catalog = payload?.["catalog"];
|
|
937
|
+
return catalog === void 0 || catalog === null || catalog === "models";
|
|
938
|
+
}
|
|
939
|
+
function onModelCatalogChanged(listener) {
|
|
940
|
+
const h = hub();
|
|
941
|
+
h.listeners.add(listener);
|
|
942
|
+
return () => {
|
|
943
|
+
h.listeners.delete(listener);
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
function modelCatalogRevision() {
|
|
947
|
+
return hub().revision;
|
|
948
|
+
}
|
|
949
|
+
function announceModelCatalogChanged(payload = {}) {
|
|
950
|
+
if (!isModelCatalogChange(payload)) return;
|
|
951
|
+
const h = hub();
|
|
952
|
+
h.revision += 1;
|
|
953
|
+
for (const l of [...h.listeners]) {
|
|
954
|
+
try {
|
|
955
|
+
l(payload);
|
|
956
|
+
} catch (err) {
|
|
957
|
+
console.error("[@ai-matrx/agents/models] a catalog_changed listener threw", err);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
function followModelCatalogChanges(subscribe) {
|
|
962
|
+
return subscribe("catalog_changed", (payload) => announceModelCatalogChanged(payload ?? {}));
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// models/label.ts
|
|
966
|
+
function modelDisplayLabel(row) {
|
|
967
|
+
if (!row) return void 0;
|
|
968
|
+
return row.common_name || row.name || void 0;
|
|
969
|
+
}
|
|
970
|
+
|
|
921
971
|
// models/catalog/records.ts
|
|
922
972
|
var classConfigKey = (modelId, offeringId) => `${modelId}:${offeringId}`;
|
|
923
973
|
var EMPTY_MODEL_RECORDS = Object.freeze({
|
|
@@ -986,7 +1036,7 @@ function withIdLists(state2) {
|
|
|
986
1036
|
const active = [];
|
|
987
1037
|
const deprecated = [];
|
|
988
1038
|
for (const [id, record] of Object.entries(state2.entities)) (record.is_deprecated === true ? deprecated : active).push(id);
|
|
989
|
-
const label = (id) => state2.entities[id]
|
|
1039
|
+
const label = (id) => modelDisplayLabel(state2.entities[id]) ?? "";
|
|
990
1040
|
const sort = (ids) => ids.sort((a, b) => label(a).localeCompare(label(b)));
|
|
991
1041
|
return { ...state2, activeIds: sort(active), deprecatedIds: sort(deprecated) };
|
|
992
1042
|
}
|
|
@@ -1407,13 +1457,13 @@ function selectDeprecatedModels(s3) {
|
|
|
1407
1457
|
var allOf = memo((entities) => Object.values(entities));
|
|
1408
1458
|
var selectAllModels = (s3) => allOf(s3.entities);
|
|
1409
1459
|
var optionsOf = memo(
|
|
1410
|
-
(models) => models.map((m) => ({ value: m.id, label: m
|
|
1460
|
+
(models) => models.map((m) => ({ value: m.id, label: modelDisplayLabel(m) || m.id, maker: m.maker, costRating: m.cost_rating, speedRating: m.speed_rating }))
|
|
1411
1461
|
);
|
|
1412
1462
|
var selectModelOptions = (s3) => optionsOf(selectActiveModels(s3));
|
|
1413
1463
|
var allOptionsOf = memo(
|
|
1414
1464
|
(entities) => Object.values(entities).map((m) => ({
|
|
1415
1465
|
value: m.id,
|
|
1416
|
-
label: m
|
|
1466
|
+
label: modelDisplayLabel(m) || m.id,
|
|
1417
1467
|
maker: m.maker,
|
|
1418
1468
|
isDeprecated: m.is_deprecated ?? false,
|
|
1419
1469
|
isRetired: m.retired_at != null,
|
|
@@ -1426,7 +1476,7 @@ function selectModelLabelById(s3, modelId) {
|
|
|
1426
1476
|
if (!modelId) return void 0;
|
|
1427
1477
|
const m = s3.entities[modelId];
|
|
1428
1478
|
const identity = s3.identityById[modelId];
|
|
1429
|
-
return m
|
|
1479
|
+
return modelDisplayLabel(m) || modelDisplayLabel(identity);
|
|
1430
1480
|
}
|
|
1431
1481
|
var selectModelNameById = (s3, modelId) => modelId ? s3.entities[modelId]?.name : void 0;
|
|
1432
1482
|
var selectModelClassName = (s3, modelId, offeringId) => modelClassName(s3.modelClassIndex, modelId, offeringId);
|
|
@@ -1444,6 +1494,18 @@ var selectDeprecatedModelsReady = (s3) => (s3.fetchScope === "deprecated" || s3.
|
|
|
1444
1494
|
var selectAllModelsReady = (s3) => s3.fetchScope === "all";
|
|
1445
1495
|
var selectModelIdentityById = (s3, modelId) => s3.identityById[modelId];
|
|
1446
1496
|
var selectModelIdentityLookupStatus = (s3, modelId) => s3.identityStatusById[modelId] ?? "idle";
|
|
1497
|
+
async function readModelIdentities(client, modelIds) {
|
|
1498
|
+
const ids = [...new Set(modelIds.filter(Boolean))];
|
|
1499
|
+
if (ids.length === 0) return [];
|
|
1500
|
+
const { data, error } = await client.schema("ai").from("model_definition").select("id, name, common_name, is_deprecated").in("id", ids);
|
|
1501
|
+
if (error) throw new Error(error.message);
|
|
1502
|
+
return Array.isArray(data) ? data : [];
|
|
1503
|
+
}
|
|
1504
|
+
async function readPublicModel(client, key) {
|
|
1505
|
+
const { data, error } = await client.schema("ai").from("model_public").select("id, name").eq(catalogKeyColumn(key), key.trim()).maybeSingle();
|
|
1506
|
+
if (error) return { row: null, error: error.message };
|
|
1507
|
+
return { row: data ?? null, error: null };
|
|
1508
|
+
}
|
|
1447
1509
|
|
|
1448
1510
|
// models/catalog/store.ts
|
|
1449
1511
|
var IDLE = { status: "idle", models: [], error: null, loadedAt: null };
|
|
@@ -1713,8 +1775,16 @@ function createModelCatalog(config) {
|
|
|
1713
1775
|
errorSink,
|
|
1714
1776
|
...config.isSignedIn ? { isSignedIn: config.isSignedIn } : {}
|
|
1715
1777
|
});
|
|
1778
|
+
const refresh = async () => {
|
|
1779
|
+
const variants = Object.keys(state2).filter((v) => state2[v].status !== "idle");
|
|
1780
|
+
await Promise.all([records.refresh(), ...variants.map((v) => run(v))]);
|
|
1781
|
+
};
|
|
1782
|
+
const stopFollowing = config.followChanges === false ? () => {
|
|
1783
|
+
} : onModelCatalogChanged(() => void refresh());
|
|
1716
1784
|
return {
|
|
1717
1785
|
records,
|
|
1786
|
+
refresh,
|
|
1787
|
+
dispose: stopFollowing,
|
|
1718
1788
|
getState: () => state2,
|
|
1719
1789
|
subscribe(listener) {
|
|
1720
1790
|
listeners.add(listener);
|
package/dist/models/index.d.cts
CHANGED
|
@@ -750,6 +750,41 @@ declare const selectDeprecatedModelsReady: (s: ModelRecordsState) => boolean;
|
|
|
750
750
|
declare const selectAllModelsReady: (s: ModelRecordsState) => boolean;
|
|
751
751
|
declare const selectModelIdentityById: (s: ModelRecordsState, modelId: string) => ModelIdentityRow | undefined;
|
|
752
752
|
declare const selectModelIdentityLookupStatus: (s: ModelRecordsState, modelId: string) => "loading" | "succeeded" | "failed" | "idle";
|
|
753
|
+
/**
|
|
754
|
+
* The durable identities (id, name, common_name, is_deprecated) of these models, read from
|
|
755
|
+
* `ai.model_definition` — the same read `records.loadIdentity` makes. For a process with no records
|
|
756
|
+
* store that stamps a name ONCE (a sync snapshot); a screen names models through the records hooks.
|
|
757
|
+
* Rows the database does not return are absent. Throws the database's error.
|
|
758
|
+
*/
|
|
759
|
+
declare function readModelIdentities(client: ModelRecordsClient, modelIds: readonly string[]): Promise<ModelIdentityRow[]>;
|
|
760
|
+
/** The structural read `readPublicModel` needs (a supabase-js client fits it as is). */
|
|
761
|
+
interface PublicModelClient {
|
|
762
|
+
schema(name: "ai"): {
|
|
763
|
+
from(table: "model_public"): {
|
|
764
|
+
select(columns: string): {
|
|
765
|
+
eq(column: string, value: string): {
|
|
766
|
+
maybeSingle(): PromiseLike<{
|
|
767
|
+
data: unknown;
|
|
768
|
+
error: {
|
|
769
|
+
message: string;
|
|
770
|
+
} | null;
|
|
771
|
+
}>;
|
|
772
|
+
};
|
|
773
|
+
};
|
|
774
|
+
};
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* One `ai.model_public` row (id + canonical name) by id or name, read fresh — for a value sent on the wire at
|
|
779
|
+
* the moment it is used (a voice session's transcription model), never for a label on screen.
|
|
780
|
+
*/
|
|
781
|
+
declare function readPublicModel(client: PublicModelClient, key: string): Promise<{
|
|
782
|
+
row: {
|
|
783
|
+
id?: unknown;
|
|
784
|
+
name?: unknown;
|
|
785
|
+
} | null;
|
|
786
|
+
error: string | null;
|
|
787
|
+
}>;
|
|
753
788
|
|
|
754
789
|
/**
|
|
755
790
|
* `@ai-matrx/agents/models` — `createModelCatalog`: the headless model catalog store.
|
|
@@ -802,6 +837,8 @@ type ModelCatalogErrorSink = (event: {
|
|
|
802
837
|
context?: object;
|
|
803
838
|
}) => void;
|
|
804
839
|
interface ModelCatalogConfig {
|
|
840
|
+
/** Re-read on every model catalog change notice (catalog/changes.ts). Default true. */
|
|
841
|
+
followChanges?: boolean;
|
|
805
842
|
client: ModelCatalogClient;
|
|
806
843
|
/** OPTIONAL — DEFAULT: a tagged `console.error` (the in-picker error banner always renders too). */
|
|
807
844
|
errorSink?: ModelCatalogErrorSink;
|
|
@@ -828,6 +865,14 @@ interface ModelCatalog {
|
|
|
828
865
|
load(variant: ModelCatalogVariant): Promise<void>;
|
|
829
866
|
/** Re-read the variant now (rows stay visible while it runs). Never throws. */
|
|
830
867
|
reload(variant: ModelCatalogVariant): Promise<void>;
|
|
868
|
+
/**
|
|
869
|
+
* The catalog changed upstream (`catalog_changed`): re-read every picker variant already read and every
|
|
870
|
+
* record already read (`records.refresh()`). Called by itself on every `announceModelCatalogChanged`
|
|
871
|
+
* (catalog/changes.ts) unless `followChanges: false`. Never throws.
|
|
872
|
+
*/
|
|
873
|
+
refresh(): Promise<void>;
|
|
874
|
+
/** Stop following `catalog_changed` (a test, or a catalog a host discards). */
|
|
875
|
+
dispose(): void;
|
|
831
876
|
/**
|
|
832
877
|
* The RECORDS part (agent core B3): options list, full records with resolved controls, class
|
|
833
878
|
* configs, the class index and identities — what chat's retired `modelRegistry` slice held.
|
|
@@ -1818,4 +1863,49 @@ catalog?: SuccessionClient): Promise<NewAgentModel>;
|
|
|
1818
1863
|
*/
|
|
1819
1864
|
declare function resolveModelMapKnob(value: unknown, catalog: SuccessionClient): Promise<Record<string, string>>;
|
|
1820
1865
|
|
|
1821
|
-
|
|
1866
|
+
/**
|
|
1867
|
+
* `@ai-matrx/agents/models` — THE model label formula. Every surface that names a model computes the
|
|
1868
|
+
* name here: the person-facing `common_name`, else the canonical `name`. An empty `common_name` counts
|
|
1869
|
+
* as absent. Pure; the row it is handed must come from the subscribed catalog (records / readModelCatalog),
|
|
1870
|
+
* so a `catalog_changed` notice re-renders it. Guard: `scripts/check-model-catalog-reads.mjs`.
|
|
1871
|
+
*/
|
|
1872
|
+
/** The two columns a model label is made from (cloud records, identities, local catalog rows). */
|
|
1873
|
+
interface ModelLabelSource {
|
|
1874
|
+
common_name?: string | null;
|
|
1875
|
+
name?: string | null;
|
|
1876
|
+
}
|
|
1877
|
+
/** A row that always has a canonical name always has a label. */
|
|
1878
|
+
declare function modelDisplayLabel(row: {
|
|
1879
|
+
common_name?: string | null;
|
|
1880
|
+
name: string;
|
|
1881
|
+
}): string;
|
|
1882
|
+
declare function modelDisplayLabel(row: ModelLabelSource | null | undefined): string | undefined;
|
|
1883
|
+
|
|
1884
|
+
/**
|
|
1885
|
+
* `@ai-matrx/agents/models` — the ONE subscription to `catalog_changed`.
|
|
1886
|
+
*
|
|
1887
|
+
* The server publishes `catalog_changed` on the platform bus for every admin edit and direct SQL write to
|
|
1888
|
+
* the model catalog. A host binds its bus client ONCE with `followModelCatalogChanges(subscribe)` (the web's
|
|
1889
|
+
* directive registry, Matrx 2's IPC forward of the main-process bus); every model catalog
|
|
1890
|
+
* (`createModelCatalog`: picker rows + records) re-reads itself on the notice, and any other reader
|
|
1891
|
+
* subscribes here (`onModelCatalogChanged`, or `useModelCatalogRevision` / `useOnModelCatalogChanged` in
|
|
1892
|
+
* `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
|
|
1893
|
+
*
|
|
1894
|
+
* The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
|
|
1895
|
+
*/
|
|
1896
|
+
/** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
|
|
1897
|
+
type ModelCatalogChangePayload = Record<string, unknown>;
|
|
1898
|
+
/** The bus client's subscribe call: `(action, handler) => unsubscribe` (the web's `registerDirectiveHandler`). */
|
|
1899
|
+
type ModelCatalogDirectiveSubscribe = (action: "catalog_changed", handler: (payload: ModelCatalogChangePayload) => void) => () => void;
|
|
1900
|
+
/** Is this notice about the MODEL catalog? (`catalog` absent or `"models"`; the tool catalog shares the action.) */
|
|
1901
|
+
declare function isModelCatalogChange(payload: ModelCatalogChangePayload | null | undefined): boolean;
|
|
1902
|
+
/** Listen for model catalog changes. Returns the unsubscribe. */
|
|
1903
|
+
declare function onModelCatalogChanged(listener: (payload: ModelCatalogChangePayload) => void): () => void;
|
|
1904
|
+
/** How many model catalog changes this page has seen (a React key / effect dependency). */
|
|
1905
|
+
declare function modelCatalogRevision(): number;
|
|
1906
|
+
/** Announce a model catalog change to every listener (a notice that is not about models is ignored). */
|
|
1907
|
+
declare function announceModelCatalogChanged(payload?: ModelCatalogChangePayload): void;
|
|
1908
|
+
/** Bind the host's bus client: every `catalog_changed` it delivers reaches every model reader. Returns the unbind. */
|
|
1909
|
+
declare function followModelCatalogChanges(subscribe: ModelCatalogDirectiveSubscribe): () => void;
|
|
1910
|
+
|
|
1911
|
+
export { type AdminOffering, type AdminPricingBand, type ArtifactUse, type AuditCapabilitiesRecord, CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, type CapabilitiesParseContext, type CatalogKeyColumn, type CatalogKindInput, type CatalogModel, type CatalogTier, type CheapestAdequateOptions, type CompatStatus, type ContentType, type CuratedCatalogClient, type CuratedModel, type CuratedPresentation, type CuratedRows, type CuratedVariant, DEFAULT_CAPABILITIES, type DecidedBy, type DeviceGraphics, type DeviceHardware, type DiscoveryAmbiguity, type DiscoveryArtifact, type DiscoveryMedia, type DiscoveryResult, type DiscoveryRuntime, type DiscoverySource, type DiscoveryVariant, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, type FeatureBucket, type FeatureBucketMeta, type FeatureKey, type FitCapabilities, GgufError, type GgufHeader, type GgufRead, type GgufTensor, type GroupedFeatures, type HardwareClass, type HardwareClasses, type HeaderFlag, type HeaderJudgement, type HeaderRefusal, INPUT_MODALITIES, INTERACTION_MODES, type Interaction, type InteractionMode, type LocalArtifact, type LocalCatalogClient, type LocalCatalogQuery, type LocalCompatRow, type LocalModel, type LocalModelCatalog, type LocalPresentation, type LocalRuntime, type LocalRuntimeBuild, type LocalVariant, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, type Modality, type ModelCapabilities, type ModelCatalog, type ModelCatalogChangePayload, type ModelCatalogClient, type ModelCatalogConfig, type ModelCatalogDirectiveSubscribe, type ModelCatalogErrorSink, type ModelCatalogPgError, type ModelCatalogPgResult, type ModelCatalogQuery, type ModelCatalogState, type ModelCatalogVariant, type ModelCatalogVariantState, type ModelClassConfig, type ModelClassEntry, type ModelClassIndex, type ModelDiagnosticEntry, type ModelDiagnosticsSink, type ModelFavorites, type ModelFavoritesCache, type ModelFavoritesClient, type ModelFavoritesConfig, type ModelFavoritesErrorSink, type ModelFavoritesState, type ModelFetchScope, type ModelFileHandle, type ModelFit, type ModelIdentityRow, type ModelJson, type ModelLabelSource, type ModelNeed, type ModelOfferingRow, type ModelOption, type ModelOptionWithLifecycle, type ModelRecord, type ModelRecordFetchType, type ModelRecords, type ModelRecordsClient, type ModelRecordsConfig, type ModelRecordsState, type ModelSelectionPurpose, type ModelSetEnv, type ModelSetFacts, type ModelSetValidation, NEW_AGENT_DEFAULT_MODEL_KNOB, type NewAgentModel, type NoSelection, OUTPUT_MODALITIES, type OrderedLike, type PreferredModelChoice, type PreferredModelChoicePorts, type PriceTier, type PricedCatalogModel, type PublicModelClient, type RankOptions, type RankedVariant, type ReadModelCatalogOptions, SERVICE_LABEL, SERVICE_LABEL_PLURAL, type SelectDevice, type Selection, type ShardName, type SuccessionClient, type SuccessionIndex, type SuccessionNode, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, type VariantDiscovery, type VariantVerdict, type Want, acceptsTextInstructions, announceModelCatalogChanged, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, cheapestAdequateModel, classConfigKey, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, followModelCatalogChanges, 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, readGgufHeader, readModelCatalog, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifacts, resetSuccessionLogForTests, 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, validateModelSet, variantVerdict, withModelClass };
|
package/dist/models/index.d.ts
CHANGED
|
@@ -750,6 +750,41 @@ declare const selectDeprecatedModelsReady: (s: ModelRecordsState) => boolean;
|
|
|
750
750
|
declare const selectAllModelsReady: (s: ModelRecordsState) => boolean;
|
|
751
751
|
declare const selectModelIdentityById: (s: ModelRecordsState, modelId: string) => ModelIdentityRow | undefined;
|
|
752
752
|
declare const selectModelIdentityLookupStatus: (s: ModelRecordsState, modelId: string) => "loading" | "succeeded" | "failed" | "idle";
|
|
753
|
+
/**
|
|
754
|
+
* The durable identities (id, name, common_name, is_deprecated) of these models, read from
|
|
755
|
+
* `ai.model_definition` — the same read `records.loadIdentity` makes. For a process with no records
|
|
756
|
+
* store that stamps a name ONCE (a sync snapshot); a screen names models through the records hooks.
|
|
757
|
+
* Rows the database does not return are absent. Throws the database's error.
|
|
758
|
+
*/
|
|
759
|
+
declare function readModelIdentities(client: ModelRecordsClient, modelIds: readonly string[]): Promise<ModelIdentityRow[]>;
|
|
760
|
+
/** The structural read `readPublicModel` needs (a supabase-js client fits it as is). */
|
|
761
|
+
interface PublicModelClient {
|
|
762
|
+
schema(name: "ai"): {
|
|
763
|
+
from(table: "model_public"): {
|
|
764
|
+
select(columns: string): {
|
|
765
|
+
eq(column: string, value: string): {
|
|
766
|
+
maybeSingle(): PromiseLike<{
|
|
767
|
+
data: unknown;
|
|
768
|
+
error: {
|
|
769
|
+
message: string;
|
|
770
|
+
} | null;
|
|
771
|
+
}>;
|
|
772
|
+
};
|
|
773
|
+
};
|
|
774
|
+
};
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* One `ai.model_public` row (id + canonical name) by id or name, read fresh — for a value sent on the wire at
|
|
779
|
+
* the moment it is used (a voice session's transcription model), never for a label on screen.
|
|
780
|
+
*/
|
|
781
|
+
declare function readPublicModel(client: PublicModelClient, key: string): Promise<{
|
|
782
|
+
row: {
|
|
783
|
+
id?: unknown;
|
|
784
|
+
name?: unknown;
|
|
785
|
+
} | null;
|
|
786
|
+
error: string | null;
|
|
787
|
+
}>;
|
|
753
788
|
|
|
754
789
|
/**
|
|
755
790
|
* `@ai-matrx/agents/models` — `createModelCatalog`: the headless model catalog store.
|
|
@@ -802,6 +837,8 @@ type ModelCatalogErrorSink = (event: {
|
|
|
802
837
|
context?: object;
|
|
803
838
|
}) => void;
|
|
804
839
|
interface ModelCatalogConfig {
|
|
840
|
+
/** Re-read on every model catalog change notice (catalog/changes.ts). Default true. */
|
|
841
|
+
followChanges?: boolean;
|
|
805
842
|
client: ModelCatalogClient;
|
|
806
843
|
/** OPTIONAL — DEFAULT: a tagged `console.error` (the in-picker error banner always renders too). */
|
|
807
844
|
errorSink?: ModelCatalogErrorSink;
|
|
@@ -828,6 +865,14 @@ interface ModelCatalog {
|
|
|
828
865
|
load(variant: ModelCatalogVariant): Promise<void>;
|
|
829
866
|
/** Re-read the variant now (rows stay visible while it runs). Never throws. */
|
|
830
867
|
reload(variant: ModelCatalogVariant): Promise<void>;
|
|
868
|
+
/**
|
|
869
|
+
* The catalog changed upstream (`catalog_changed`): re-read every picker variant already read and every
|
|
870
|
+
* record already read (`records.refresh()`). Called by itself on every `announceModelCatalogChanged`
|
|
871
|
+
* (catalog/changes.ts) unless `followChanges: false`. Never throws.
|
|
872
|
+
*/
|
|
873
|
+
refresh(): Promise<void>;
|
|
874
|
+
/** Stop following `catalog_changed` (a test, or a catalog a host discards). */
|
|
875
|
+
dispose(): void;
|
|
831
876
|
/**
|
|
832
877
|
* The RECORDS part (agent core B3): options list, full records with resolved controls, class
|
|
833
878
|
* configs, the class index and identities — what chat's retired `modelRegistry` slice held.
|
|
@@ -1818,4 +1863,49 @@ catalog?: SuccessionClient): Promise<NewAgentModel>;
|
|
|
1818
1863
|
*/
|
|
1819
1864
|
declare function resolveModelMapKnob(value: unknown, catalog: SuccessionClient): Promise<Record<string, string>>;
|
|
1820
1865
|
|
|
1821
|
-
|
|
1866
|
+
/**
|
|
1867
|
+
* `@ai-matrx/agents/models` — THE model label formula. Every surface that names a model computes the
|
|
1868
|
+
* name here: the person-facing `common_name`, else the canonical `name`. An empty `common_name` counts
|
|
1869
|
+
* as absent. Pure; the row it is handed must come from the subscribed catalog (records / readModelCatalog),
|
|
1870
|
+
* so a `catalog_changed` notice re-renders it. Guard: `scripts/check-model-catalog-reads.mjs`.
|
|
1871
|
+
*/
|
|
1872
|
+
/** The two columns a model label is made from (cloud records, identities, local catalog rows). */
|
|
1873
|
+
interface ModelLabelSource {
|
|
1874
|
+
common_name?: string | null;
|
|
1875
|
+
name?: string | null;
|
|
1876
|
+
}
|
|
1877
|
+
/** A row that always has a canonical name always has a label. */
|
|
1878
|
+
declare function modelDisplayLabel(row: {
|
|
1879
|
+
common_name?: string | null;
|
|
1880
|
+
name: string;
|
|
1881
|
+
}): string;
|
|
1882
|
+
declare function modelDisplayLabel(row: ModelLabelSource | null | undefined): string | undefined;
|
|
1883
|
+
|
|
1884
|
+
/**
|
|
1885
|
+
* `@ai-matrx/agents/models` — the ONE subscription to `catalog_changed`.
|
|
1886
|
+
*
|
|
1887
|
+
* The server publishes `catalog_changed` on the platform bus for every admin edit and direct SQL write to
|
|
1888
|
+
* the model catalog. A host binds its bus client ONCE with `followModelCatalogChanges(subscribe)` (the web's
|
|
1889
|
+
* directive registry, Matrx 2's IPC forward of the main-process bus); every model catalog
|
|
1890
|
+
* (`createModelCatalog`: picker rows + records) re-reads itself on the notice, and any other reader
|
|
1891
|
+
* subscribes here (`onModelCatalogChanged`, or `useModelCatalogRevision` / `useOnModelCatalogChanged` in
|
|
1892
|
+
* `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
|
|
1893
|
+
*
|
|
1894
|
+
* The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
|
|
1895
|
+
*/
|
|
1896
|
+
/** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
|
|
1897
|
+
type ModelCatalogChangePayload = Record<string, unknown>;
|
|
1898
|
+
/** The bus client's subscribe call: `(action, handler) => unsubscribe` (the web's `registerDirectiveHandler`). */
|
|
1899
|
+
type ModelCatalogDirectiveSubscribe = (action: "catalog_changed", handler: (payload: ModelCatalogChangePayload) => void) => () => void;
|
|
1900
|
+
/** Is this notice about the MODEL catalog? (`catalog` absent or `"models"`; the tool catalog shares the action.) */
|
|
1901
|
+
declare function isModelCatalogChange(payload: ModelCatalogChangePayload | null | undefined): boolean;
|
|
1902
|
+
/** Listen for model catalog changes. Returns the unsubscribe. */
|
|
1903
|
+
declare function onModelCatalogChanged(listener: (payload: ModelCatalogChangePayload) => void): () => void;
|
|
1904
|
+
/** How many model catalog changes this page has seen (a React key / effect dependency). */
|
|
1905
|
+
declare function modelCatalogRevision(): number;
|
|
1906
|
+
/** Announce a model catalog change to every listener (a notice that is not about models is ignored). */
|
|
1907
|
+
declare function announceModelCatalogChanged(payload?: ModelCatalogChangePayload): void;
|
|
1908
|
+
/** Bind the host's bus client: every `catalog_changed` it delivers reaches every model reader. Returns the unbind. */
|
|
1909
|
+
declare function followModelCatalogChanges(subscribe: ModelCatalogDirectiveSubscribe): () => void;
|
|
1910
|
+
|
|
1911
|
+
export { type AdminOffering, type AdminPricingBand, type ArtifactUse, type AuditCapabilitiesRecord, CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, type CapabilitiesParseContext, type CatalogKeyColumn, type CatalogKindInput, type CatalogModel, type CatalogTier, type CheapestAdequateOptions, type CompatStatus, type ContentType, type CuratedCatalogClient, type CuratedModel, type CuratedPresentation, type CuratedRows, type CuratedVariant, DEFAULT_CAPABILITIES, type DecidedBy, type DeviceGraphics, type DeviceHardware, type DiscoveryAmbiguity, type DiscoveryArtifact, type DiscoveryMedia, type DiscoveryResult, type DiscoveryRuntime, type DiscoverySource, type DiscoveryVariant, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, type FeatureBucket, type FeatureBucketMeta, type FeatureKey, type FitCapabilities, GgufError, type GgufHeader, type GgufRead, type GgufTensor, type GroupedFeatures, type HardwareClass, type HardwareClasses, type HeaderFlag, type HeaderJudgement, type HeaderRefusal, INPUT_MODALITIES, INTERACTION_MODES, type Interaction, type InteractionMode, type LocalArtifact, type LocalCatalogClient, type LocalCatalogQuery, type LocalCompatRow, type LocalModel, type LocalModelCatalog, type LocalPresentation, type LocalRuntime, type LocalRuntimeBuild, type LocalVariant, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, type Modality, type ModelCapabilities, type ModelCatalog, type ModelCatalogChangePayload, type ModelCatalogClient, type ModelCatalogConfig, type ModelCatalogDirectiveSubscribe, type ModelCatalogErrorSink, type ModelCatalogPgError, type ModelCatalogPgResult, type ModelCatalogQuery, type ModelCatalogState, type ModelCatalogVariant, type ModelCatalogVariantState, type ModelClassConfig, type ModelClassEntry, type ModelClassIndex, type ModelDiagnosticEntry, type ModelDiagnosticsSink, type ModelFavorites, type ModelFavoritesCache, type ModelFavoritesClient, type ModelFavoritesConfig, type ModelFavoritesErrorSink, type ModelFavoritesState, type ModelFetchScope, type ModelFileHandle, type ModelFit, type ModelIdentityRow, type ModelJson, type ModelLabelSource, type ModelNeed, type ModelOfferingRow, type ModelOption, type ModelOptionWithLifecycle, type ModelRecord, type ModelRecordFetchType, type ModelRecords, type ModelRecordsClient, type ModelRecordsConfig, type ModelRecordsState, type ModelSelectionPurpose, type ModelSetEnv, type ModelSetFacts, type ModelSetValidation, NEW_AGENT_DEFAULT_MODEL_KNOB, type NewAgentModel, type NoSelection, OUTPUT_MODALITIES, type OrderedLike, type PreferredModelChoice, type PreferredModelChoicePorts, type PriceTier, type PricedCatalogModel, type PublicModelClient, type RankOptions, type RankedVariant, type ReadModelCatalogOptions, SERVICE_LABEL, SERVICE_LABEL_PLURAL, type SelectDevice, type Selection, type ShardName, type SuccessionClient, type SuccessionIndex, type SuccessionNode, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, type VariantDiscovery, type VariantVerdict, type Want, acceptsTextInstructions, announceModelCatalogChanged, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, cheapestAdequateModel, classConfigKey, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, followModelCatalogChanges, 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, readGgufHeader, readModelCatalog, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifacts, resetSuccessionLogForTests, 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, validateModelSet, variantVerdict, withModelClass };
|
package/dist/models/index.js
CHANGED
|
@@ -759,6 +759,48 @@ var OUTPUT_MODALITIES = [
|
|
|
759
759
|
"decision"
|
|
760
760
|
];
|
|
761
761
|
|
|
762
|
+
// models/catalog/changes.ts
|
|
763
|
+
var KEY2 = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
|
|
764
|
+
function hub() {
|
|
765
|
+
const g = globalThis;
|
|
766
|
+
return g[KEY2] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
|
|
767
|
+
}
|
|
768
|
+
function isModelCatalogChange(payload) {
|
|
769
|
+
const catalog = payload?.["catalog"];
|
|
770
|
+
return catalog === void 0 || catalog === null || catalog === "models";
|
|
771
|
+
}
|
|
772
|
+
function onModelCatalogChanged(listener) {
|
|
773
|
+
const h = hub();
|
|
774
|
+
h.listeners.add(listener);
|
|
775
|
+
return () => {
|
|
776
|
+
h.listeners.delete(listener);
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function modelCatalogRevision() {
|
|
780
|
+
return hub().revision;
|
|
781
|
+
}
|
|
782
|
+
function announceModelCatalogChanged(payload = {}) {
|
|
783
|
+
if (!isModelCatalogChange(payload)) return;
|
|
784
|
+
const h = hub();
|
|
785
|
+
h.revision += 1;
|
|
786
|
+
for (const l of [...h.listeners]) {
|
|
787
|
+
try {
|
|
788
|
+
l(payload);
|
|
789
|
+
} catch (err) {
|
|
790
|
+
console.error("[@ai-matrx/agents/models] a catalog_changed listener threw", err);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
function followModelCatalogChanges(subscribe) {
|
|
795
|
+
return subscribe("catalog_changed", (payload) => announceModelCatalogChanged(payload ?? {}));
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// models/label.ts
|
|
799
|
+
function modelDisplayLabel(row) {
|
|
800
|
+
if (!row) return void 0;
|
|
801
|
+
return row.common_name || row.name || void 0;
|
|
802
|
+
}
|
|
803
|
+
|
|
762
804
|
// models/catalog/records.ts
|
|
763
805
|
var classConfigKey = (modelId, offeringId) => `${modelId}:${offeringId}`;
|
|
764
806
|
var EMPTY_MODEL_RECORDS = Object.freeze({
|
|
@@ -827,7 +869,7 @@ function withIdLists(state2) {
|
|
|
827
869
|
const active = [];
|
|
828
870
|
const deprecated = [];
|
|
829
871
|
for (const [id, record] of Object.entries(state2.entities)) (record.is_deprecated === true ? deprecated : active).push(id);
|
|
830
|
-
const label = (id) => state2.entities[id]
|
|
872
|
+
const label = (id) => modelDisplayLabel(state2.entities[id]) ?? "";
|
|
831
873
|
const sort = (ids) => ids.sort((a, b) => label(a).localeCompare(label(b)));
|
|
832
874
|
return { ...state2, activeIds: sort(active), deprecatedIds: sort(deprecated) };
|
|
833
875
|
}
|
|
@@ -1248,13 +1290,13 @@ function selectDeprecatedModels(s3) {
|
|
|
1248
1290
|
var allOf = memo((entities) => Object.values(entities));
|
|
1249
1291
|
var selectAllModels = (s3) => allOf(s3.entities);
|
|
1250
1292
|
var optionsOf = memo(
|
|
1251
|
-
(models) => models.map((m) => ({ value: m.id, label: m
|
|
1293
|
+
(models) => models.map((m) => ({ value: m.id, label: modelDisplayLabel(m) || m.id, maker: m.maker, costRating: m.cost_rating, speedRating: m.speed_rating }))
|
|
1252
1294
|
);
|
|
1253
1295
|
var selectModelOptions = (s3) => optionsOf(selectActiveModels(s3));
|
|
1254
1296
|
var allOptionsOf = memo(
|
|
1255
1297
|
(entities) => Object.values(entities).map((m) => ({
|
|
1256
1298
|
value: m.id,
|
|
1257
|
-
label: m
|
|
1299
|
+
label: modelDisplayLabel(m) || m.id,
|
|
1258
1300
|
maker: m.maker,
|
|
1259
1301
|
isDeprecated: m.is_deprecated ?? false,
|
|
1260
1302
|
isRetired: m.retired_at != null,
|
|
@@ -1267,7 +1309,7 @@ function selectModelLabelById(s3, modelId) {
|
|
|
1267
1309
|
if (!modelId) return void 0;
|
|
1268
1310
|
const m = s3.entities[modelId];
|
|
1269
1311
|
const identity = s3.identityById[modelId];
|
|
1270
|
-
return m
|
|
1312
|
+
return modelDisplayLabel(m) || modelDisplayLabel(identity);
|
|
1271
1313
|
}
|
|
1272
1314
|
var selectModelNameById = (s3, modelId) => modelId ? s3.entities[modelId]?.name : void 0;
|
|
1273
1315
|
var selectModelClassName = (s3, modelId, offeringId) => modelClassName(s3.modelClassIndex, modelId, offeringId);
|
|
@@ -1285,6 +1327,18 @@ var selectDeprecatedModelsReady = (s3) => (s3.fetchScope === "deprecated" || s3.
|
|
|
1285
1327
|
var selectAllModelsReady = (s3) => s3.fetchScope === "all";
|
|
1286
1328
|
var selectModelIdentityById = (s3, modelId) => s3.identityById[modelId];
|
|
1287
1329
|
var selectModelIdentityLookupStatus = (s3, modelId) => s3.identityStatusById[modelId] ?? "idle";
|
|
1330
|
+
async function readModelIdentities(client, modelIds) {
|
|
1331
|
+
const ids = [...new Set(modelIds.filter(Boolean))];
|
|
1332
|
+
if (ids.length === 0) return [];
|
|
1333
|
+
const { data, error } = await client.schema("ai").from("model_definition").select("id, name, common_name, is_deprecated").in("id", ids);
|
|
1334
|
+
if (error) throw new Error(error.message);
|
|
1335
|
+
return Array.isArray(data) ? data : [];
|
|
1336
|
+
}
|
|
1337
|
+
async function readPublicModel(client, key) {
|
|
1338
|
+
const { data, error } = await client.schema("ai").from("model_public").select("id, name").eq(catalogKeyColumn(key), key.trim()).maybeSingle();
|
|
1339
|
+
if (error) return { row: null, error: error.message };
|
|
1340
|
+
return { row: data ?? null, error: null };
|
|
1341
|
+
}
|
|
1288
1342
|
|
|
1289
1343
|
// models/catalog/store.ts
|
|
1290
1344
|
var IDLE = { status: "idle", models: [], error: null, loadedAt: null };
|
|
@@ -1554,8 +1608,16 @@ function createModelCatalog(config) {
|
|
|
1554
1608
|
errorSink,
|
|
1555
1609
|
...config.isSignedIn ? { isSignedIn: config.isSignedIn } : {}
|
|
1556
1610
|
});
|
|
1611
|
+
const refresh = async () => {
|
|
1612
|
+
const variants = Object.keys(state2).filter((v) => state2[v].status !== "idle");
|
|
1613
|
+
await Promise.all([records.refresh(), ...variants.map((v) => run(v))]);
|
|
1614
|
+
};
|
|
1615
|
+
const stopFollowing = config.followChanges === false ? () => {
|
|
1616
|
+
} : onModelCatalogChanged(() => void refresh());
|
|
1557
1617
|
return {
|
|
1558
1618
|
records,
|
|
1619
|
+
refresh,
|
|
1620
|
+
dispose: stopFollowing,
|
|
1559
1621
|
getState: () => state2,
|
|
1560
1622
|
subscribe(listener) {
|
|
1561
1623
|
listeners.add(listener);
|
|
@@ -2771,6 +2833,7 @@ export {
|
|
|
2771
2833
|
TOP_TIER_COST_RATING,
|
|
2772
2834
|
TOP_TIER_LOCKED_TITLE,
|
|
2773
2835
|
acceptsTextInstructions,
|
|
2836
|
+
announceModelCatalogChanged,
|
|
2774
2837
|
blendedPrice,
|
|
2775
2838
|
buildModelClassIndex,
|
|
2776
2839
|
buildSuccessionIndex,
|
|
@@ -2794,6 +2857,7 @@ export {
|
|
|
2794
2857
|
fitsMemory,
|
|
2795
2858
|
fitsNeed,
|
|
2796
2859
|
fitsThisComputer,
|
|
2860
|
+
followModelCatalogChanges,
|
|
2797
2861
|
formatMemoryBytes,
|
|
2798
2862
|
fromDiscovery,
|
|
2799
2863
|
ggufEngine,
|
|
@@ -2811,21 +2875,25 @@ export {
|
|
|
2811
2875
|
isListedVariant,
|
|
2812
2876
|
isLocalModelCatalog,
|
|
2813
2877
|
isManagedAgentModelCapability,
|
|
2878
|
+
isModelCatalogChange,
|
|
2814
2879
|
isProjectorName,
|
|
2815
2880
|
isSupersedable,
|
|
2816
2881
|
isTopTierRating,
|
|
2817
2882
|
judgeHeaderFacts,
|
|
2818
2883
|
mergeAuditRecordIntoCapabilities,
|
|
2819
2884
|
missingShards,
|
|
2885
|
+
modelCatalogRevision,
|
|
2820
2886
|
modelClassKnobFor,
|
|
2821
2887
|
modelClassName,
|
|
2822
2888
|
modelClassPinOf,
|
|
2889
|
+
modelDisplayLabel,
|
|
2823
2890
|
modelsForSelectionPurpose,
|
|
2824
2891
|
normalizeArch,
|
|
2825
2892
|
normalizeModel,
|
|
2826
2893
|
normalizeModels,
|
|
2827
2894
|
normalizeOs,
|
|
2828
2895
|
normalizePromptSettings,
|
|
2896
|
+
onModelCatalogChanged,
|
|
2829
2897
|
orderCatalogGroups,
|
|
2830
2898
|
orderCatalogModels,
|
|
2831
2899
|
outputFormatControlKey,
|
|
@@ -2837,6 +2905,8 @@ export {
|
|
|
2837
2905
|
readCuratedRows,
|
|
2838
2906
|
readGgufHeader,
|
|
2839
2907
|
readModelCatalog,
|
|
2908
|
+
readModelIdentities,
|
|
2909
|
+
readPublicModel,
|
|
2840
2910
|
reconcileModelFavoriteIds,
|
|
2841
2911
|
reportModelDiagnostic,
|
|
2842
2912
|
requireCanonicalCapabilities,
|
|
@@ -29,10 +29,12 @@ __export(react_exports, {
|
|
|
29
29
|
useLocalModels: () => useLocalModels,
|
|
30
30
|
useModelAccess: () => useModelAccess,
|
|
31
31
|
useModelCatalog: () => useModelCatalog,
|
|
32
|
+
useModelCatalogRevision: () => useModelCatalogRevision,
|
|
32
33
|
useModelCatalogStore: () => useModelCatalogStore,
|
|
33
34
|
useModelCatalogUiPorts: () => useModelCatalogUiPorts,
|
|
34
35
|
useModelFavorites: () => useModelFavorites,
|
|
35
|
-
useModelRecordsSelector: () => useModelRecordsSelector
|
|
36
|
+
useModelRecordsSelector: () => useModelRecordsSelector,
|
|
37
|
+
useOnModelCatalogChanged: () => useOnModelCatalogChanged
|
|
36
38
|
});
|
|
37
39
|
module.exports = __toCommonJS(react_exports);
|
|
38
40
|
|
|
@@ -2289,6 +2291,25 @@ function ModelListDropdown({
|
|
|
2289
2291
|
|
|
2290
2292
|
// models/react/records.ts
|
|
2291
2293
|
var import_react3 = require("react");
|
|
2294
|
+
|
|
2295
|
+
// models/catalog/changes.ts
|
|
2296
|
+
var KEY = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
|
|
2297
|
+
function hub() {
|
|
2298
|
+
const g = globalThis;
|
|
2299
|
+
return g[KEY] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
|
|
2300
|
+
}
|
|
2301
|
+
function onModelCatalogChanged(listener) {
|
|
2302
|
+
const h = hub();
|
|
2303
|
+
h.listeners.add(listener);
|
|
2304
|
+
return () => {
|
|
2305
|
+
h.listeners.delete(listener);
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
function modelCatalogRevision() {
|
|
2309
|
+
return hub().revision;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
// models/react/records.ts
|
|
2292
2313
|
function useModelRecordsSelector(records, select) {
|
|
2293
2314
|
return (0, import_react3.useSyncExternalStore)(
|
|
2294
2315
|
records.subscribe,
|
|
@@ -2296,6 +2317,15 @@ function useModelRecordsSelector(records, select) {
|
|
|
2296
2317
|
() => select(records.getState())
|
|
2297
2318
|
);
|
|
2298
2319
|
}
|
|
2320
|
+
var subscribeRevision = (listener) => onModelCatalogChanged(() => listener());
|
|
2321
|
+
function useModelCatalogRevision() {
|
|
2322
|
+
return (0, import_react3.useSyncExternalStore)(subscribeRevision, modelCatalogRevision, modelCatalogRevision);
|
|
2323
|
+
}
|
|
2324
|
+
function useOnModelCatalogChanged(onChange) {
|
|
2325
|
+
const latest = (0, import_react3.useRef)(onChange);
|
|
2326
|
+
latest.current = onChange;
|
|
2327
|
+
(0, import_react3.useEffect)(() => onModelCatalogChanged((payload) => latest.current(payload)), []);
|
|
2328
|
+
}
|
|
2299
2329
|
|
|
2300
2330
|
// models/react/ModelDiscovery.tsx
|
|
2301
2331
|
var import_format3 = require("@ai-matrx/kit/format");
|
|
@@ -398,6 +398,14 @@ interface ModelCatalog {
|
|
|
398
398
|
load(variant: ModelCatalogVariant): Promise<void>;
|
|
399
399
|
/** Re-read the variant now (rows stay visible while it runs). Never throws. */
|
|
400
400
|
reload(variant: ModelCatalogVariant): Promise<void>;
|
|
401
|
+
/**
|
|
402
|
+
* The catalog changed upstream (`catalog_changed`): re-read every picker variant already read and every
|
|
403
|
+
* record already read (`records.refresh()`). Called by itself on every `announceModelCatalogChanged`
|
|
404
|
+
* (catalog/changes.ts) unless `followChanges: false`. Never throws.
|
|
405
|
+
*/
|
|
406
|
+
refresh(): Promise<void>;
|
|
407
|
+
/** Stop following `catalog_changed` (a test, or a catalog a host discards). */
|
|
408
|
+
dispose(): void;
|
|
401
409
|
/**
|
|
402
410
|
* The RECORDS part (agent core B3): options list, full records with resolved controls, class
|
|
403
411
|
* configs, the class index and identities — what chat's retired `modelRegistry` slice held.
|
|
@@ -650,8 +658,30 @@ interface ModelListDropdownBaseProps {
|
|
|
650
658
|
}
|
|
651
659
|
declare function ModelListDropdown({ value: savedValue, localModels: localModelsProp, access: accessProp, onValueChange, inputModalities, outputModalities, selectionPurpose, allowedModelIds, catalogVariant, emptyOptionLabel, onClear, placeholder, priorityModelIds, pinnedOfferingId, onOfferingPinChange, modelOnly, className, triggerVariant, presentation, id, disabled, disabledTitle, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }: ModelListDropdownProps): react.JSX.Element;
|
|
652
660
|
|
|
661
|
+
/**
|
|
662
|
+
* `@ai-matrx/agents/models` — the ONE subscription to `catalog_changed`.
|
|
663
|
+
*
|
|
664
|
+
* The server publishes `catalog_changed` on the platform bus for every admin edit and direct SQL write to
|
|
665
|
+
* the model catalog. A host binds its bus client ONCE with `followModelCatalogChanges(subscribe)` (the web's
|
|
666
|
+
* directive registry, Matrx 2's IPC forward of the main-process bus); every model catalog
|
|
667
|
+
* (`createModelCatalog`: picker rows + records) re-reads itself on the notice, and any other reader
|
|
668
|
+
* subscribes here (`onModelCatalogChanged`, or `useModelCatalogRevision` / `useOnModelCatalogChanged` in
|
|
669
|
+
* `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
|
|
670
|
+
*
|
|
671
|
+
* The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
|
|
672
|
+
*/
|
|
673
|
+
/** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
|
|
674
|
+
type ModelCatalogChangePayload = Record<string, unknown>;
|
|
675
|
+
|
|
653
676
|
/** Subscribe to a slice of a model catalog's records. `select` must return a stable value for unchanged state. */
|
|
654
677
|
declare function useModelRecordsSelector<T>(records: ModelRecords, select: (state: ModelRecordsState) => T): T;
|
|
678
|
+
/**
|
|
679
|
+
* How many `catalog_changed` notices this page has seen. A reader that holds its OWN rows (the admin model
|
|
680
|
+
* editor) puts it in its effect dependencies / query key, so an edit made anywhere re-reads it.
|
|
681
|
+
*/
|
|
682
|
+
declare function useModelCatalogRevision(): number;
|
|
683
|
+
/** Run `onChange` on every model catalog change notice (the latest callback; never stale). */
|
|
684
|
+
declare function useOnModelCatalogChanged(onChange: (payload: ModelCatalogChangePayload) => void): void;
|
|
655
685
|
|
|
656
686
|
/**
|
|
657
687
|
* `@ai-matrx/agents/models` — downloadable (local) models: the catalog shape every client reads, the
|
|
@@ -1010,4 +1040,4 @@ interface DiscoveryResultViewProps {
|
|
|
1010
1040
|
/** A resolved model: its header, the source's reason when the originals will not run, and every version judged. */
|
|
1011
1041
|
declare function DiscoveryResultView({ result, device, runtimes, want, chooseLabel, mode, onChoose, renderVariantActions, renderActions }: DiscoveryResultViewProps): react.JSX.Element;
|
|
1012
1042
|
|
|
1013
|
-
export { type DiscoveryKind, type DiscoveryMode, type DiscoveryPort, DiscoveryResultView, type DiscoveryResultViewProps, type DiscoverySearchHit, type DiscoverySearchPage, type DiscoverySearchQuery, type DiscoverySort, type LocalModelEntry, type LocalModelsPort, type ModelAccessPort, type ModelCatalogLinkComponent, ModelCatalogProvider, type ModelCatalogProviderProps, type ModelCatalogUiPorts, ModelDiscovery, type ModelDiscoveryProps, ModelListDropdown, type ModelListDropdownProps, YOUR_KEY_LABEL, useLocalModels, useModelAccess, useModelCatalog, useModelCatalogStore, useModelCatalogUiPorts, useModelFavorites, useModelRecordsSelector };
|
|
1043
|
+
export { type DiscoveryKind, type DiscoveryMode, type DiscoveryPort, DiscoveryResultView, type DiscoveryResultViewProps, type DiscoverySearchHit, type DiscoverySearchPage, type DiscoverySearchQuery, type DiscoverySort, type LocalModelEntry, type LocalModelsPort, type ModelAccessPort, type ModelCatalogLinkComponent, ModelCatalogProvider, type ModelCatalogProviderProps, type ModelCatalogUiPorts, ModelDiscovery, type ModelDiscoveryProps, ModelListDropdown, type ModelListDropdownProps, YOUR_KEY_LABEL, useLocalModels, useModelAccess, useModelCatalog, useModelCatalogRevision, useModelCatalogStore, useModelCatalogUiPorts, useModelFavorites, useModelRecordsSelector, useOnModelCatalogChanged };
|
|
@@ -398,6 +398,14 @@ interface ModelCatalog {
|
|
|
398
398
|
load(variant: ModelCatalogVariant): Promise<void>;
|
|
399
399
|
/** Re-read the variant now (rows stay visible while it runs). Never throws. */
|
|
400
400
|
reload(variant: ModelCatalogVariant): Promise<void>;
|
|
401
|
+
/**
|
|
402
|
+
* The catalog changed upstream (`catalog_changed`): re-read every picker variant already read and every
|
|
403
|
+
* record already read (`records.refresh()`). Called by itself on every `announceModelCatalogChanged`
|
|
404
|
+
* (catalog/changes.ts) unless `followChanges: false`. Never throws.
|
|
405
|
+
*/
|
|
406
|
+
refresh(): Promise<void>;
|
|
407
|
+
/** Stop following `catalog_changed` (a test, or a catalog a host discards). */
|
|
408
|
+
dispose(): void;
|
|
401
409
|
/**
|
|
402
410
|
* The RECORDS part (agent core B3): options list, full records with resolved controls, class
|
|
403
411
|
* configs, the class index and identities — what chat's retired `modelRegistry` slice held.
|
|
@@ -650,8 +658,30 @@ interface ModelListDropdownBaseProps {
|
|
|
650
658
|
}
|
|
651
659
|
declare function ModelListDropdown({ value: savedValue, localModels: localModelsProp, access: accessProp, onValueChange, inputModalities, outputModalities, selectionPurpose, allowedModelIds, catalogVariant, emptyOptionLabel, onClear, placeholder, priorityModelIds, pinnedOfferingId, onOfferingPinChange, modelOnly, className, triggerVariant, presentation, id, disabled, disabledTitle, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }: ModelListDropdownProps): react.JSX.Element;
|
|
652
660
|
|
|
661
|
+
/**
|
|
662
|
+
* `@ai-matrx/agents/models` — the ONE subscription to `catalog_changed`.
|
|
663
|
+
*
|
|
664
|
+
* The server publishes `catalog_changed` on the platform bus for every admin edit and direct SQL write to
|
|
665
|
+
* the model catalog. A host binds its bus client ONCE with `followModelCatalogChanges(subscribe)` (the web's
|
|
666
|
+
* directive registry, Matrx 2's IPC forward of the main-process bus); every model catalog
|
|
667
|
+
* (`createModelCatalog`: picker rows + records) re-reads itself on the notice, and any other reader
|
|
668
|
+
* subscribes here (`onModelCatalogChanged`, or `useModelCatalogRevision` / `useOnModelCatalogChanged` in
|
|
669
|
+
* `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
|
|
670
|
+
*
|
|
671
|
+
* The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
|
|
672
|
+
*/
|
|
673
|
+
/** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
|
|
674
|
+
type ModelCatalogChangePayload = Record<string, unknown>;
|
|
675
|
+
|
|
653
676
|
/** Subscribe to a slice of a model catalog's records. `select` must return a stable value for unchanged state. */
|
|
654
677
|
declare function useModelRecordsSelector<T>(records: ModelRecords, select: (state: ModelRecordsState) => T): T;
|
|
678
|
+
/**
|
|
679
|
+
* How many `catalog_changed` notices this page has seen. A reader that holds its OWN rows (the admin model
|
|
680
|
+
* editor) puts it in its effect dependencies / query key, so an edit made anywhere re-reads it.
|
|
681
|
+
*/
|
|
682
|
+
declare function useModelCatalogRevision(): number;
|
|
683
|
+
/** Run `onChange` on every model catalog change notice (the latest callback; never stale). */
|
|
684
|
+
declare function useOnModelCatalogChanged(onChange: (payload: ModelCatalogChangePayload) => void): void;
|
|
655
685
|
|
|
656
686
|
/**
|
|
657
687
|
* `@ai-matrx/agents/models` — downloadable (local) models: the catalog shape every client reads, the
|
|
@@ -1010,4 +1040,4 @@ interface DiscoveryResultViewProps {
|
|
|
1010
1040
|
/** A resolved model: its header, the source's reason when the originals will not run, and every version judged. */
|
|
1011
1041
|
declare function DiscoveryResultView({ result, device, runtimes, want, chooseLabel, mode, onChoose, renderVariantActions, renderActions }: DiscoveryResultViewProps): react.JSX.Element;
|
|
1012
1042
|
|
|
1013
|
-
export { type DiscoveryKind, type DiscoveryMode, type DiscoveryPort, DiscoveryResultView, type DiscoveryResultViewProps, type DiscoverySearchHit, type DiscoverySearchPage, type DiscoverySearchQuery, type DiscoverySort, type LocalModelEntry, type LocalModelsPort, type ModelAccessPort, type ModelCatalogLinkComponent, ModelCatalogProvider, type ModelCatalogProviderProps, type ModelCatalogUiPorts, ModelDiscovery, type ModelDiscoveryProps, ModelListDropdown, type ModelListDropdownProps, YOUR_KEY_LABEL, useLocalModels, useModelAccess, useModelCatalog, useModelCatalogStore, useModelCatalogUiPorts, useModelFavorites, useModelRecordsSelector };
|
|
1043
|
+
export { type DiscoveryKind, type DiscoveryMode, type DiscoveryPort, DiscoveryResultView, type DiscoveryResultViewProps, type DiscoverySearchHit, type DiscoverySearchPage, type DiscoverySearchQuery, type DiscoverySort, type LocalModelEntry, type LocalModelsPort, type ModelAccessPort, type ModelCatalogLinkComponent, ModelCatalogProvider, type ModelCatalogProviderProps, type ModelCatalogUiPorts, ModelDiscovery, type ModelDiscoveryProps, ModelListDropdown, type ModelListDropdownProps, YOUR_KEY_LABEL, useLocalModels, useModelAccess, useModelCatalog, useModelCatalogRevision, useModelCatalogStore, useModelCatalogUiPorts, useModelFavorites, useModelRecordsSelector, useOnModelCatalogChanged };
|
|
@@ -2280,7 +2280,26 @@ function ModelListDropdown({
|
|
|
2280
2280
|
}
|
|
2281
2281
|
|
|
2282
2282
|
// models/react/records.ts
|
|
2283
|
-
import { useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
2283
|
+
import { useEffect as useEffect3, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
2284
|
+
|
|
2285
|
+
// models/catalog/changes.ts
|
|
2286
|
+
var KEY = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
|
|
2287
|
+
function hub() {
|
|
2288
|
+
const g = globalThis;
|
|
2289
|
+
return g[KEY] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
|
|
2290
|
+
}
|
|
2291
|
+
function onModelCatalogChanged(listener) {
|
|
2292
|
+
const h = hub();
|
|
2293
|
+
h.listeners.add(listener);
|
|
2294
|
+
return () => {
|
|
2295
|
+
h.listeners.delete(listener);
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
function modelCatalogRevision() {
|
|
2299
|
+
return hub().revision;
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
// models/react/records.ts
|
|
2284
2303
|
function useModelRecordsSelector(records, select) {
|
|
2285
2304
|
return useSyncExternalStore2(
|
|
2286
2305
|
records.subscribe,
|
|
@@ -2288,10 +2307,19 @@ function useModelRecordsSelector(records, select) {
|
|
|
2288
2307
|
() => select(records.getState())
|
|
2289
2308
|
);
|
|
2290
2309
|
}
|
|
2310
|
+
var subscribeRevision = (listener) => onModelCatalogChanged(() => listener());
|
|
2311
|
+
function useModelCatalogRevision() {
|
|
2312
|
+
return useSyncExternalStore2(subscribeRevision, modelCatalogRevision, modelCatalogRevision);
|
|
2313
|
+
}
|
|
2314
|
+
function useOnModelCatalogChanged(onChange) {
|
|
2315
|
+
const latest = useRef2(onChange);
|
|
2316
|
+
latest.current = onChange;
|
|
2317
|
+
useEffect3(() => onModelCatalogChanged((payload) => latest.current(payload)), []);
|
|
2318
|
+
}
|
|
2291
2319
|
|
|
2292
2320
|
// models/react/ModelDiscovery.tsx
|
|
2293
2321
|
import { formatCount as formatCount2, formatFileSize } from "@ai-matrx/kit/format";
|
|
2294
|
-
import { useEffect as
|
|
2322
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
|
|
2295
2323
|
import { Avatar, AvatarFallback, AvatarImage, Collapsible, CollapsibleContent, CollapsibleTrigger, ErrorNotice } from "@ai-matrx/design-system";
|
|
2296
2324
|
import { Badge, Button, Field, SearchField, Select, Tabs } from "@ai-matrx/design-system/controls";
|
|
2297
2325
|
|
|
@@ -2570,8 +2598,8 @@ function ModelDiscovery({
|
|
|
2570
2598
|
const result = controlled !== void 0 ? controlled : own;
|
|
2571
2599
|
const [resolving, setResolving] = useState2(null);
|
|
2572
2600
|
const [resolveError, setResolveError] = useState2(null);
|
|
2573
|
-
const abortRef =
|
|
2574
|
-
|
|
2601
|
+
const abortRef = useRef3(null);
|
|
2602
|
+
useEffect4(() => () => abortRef.current?.abort(), []);
|
|
2575
2603
|
const resolve = async (link) => {
|
|
2576
2604
|
const trimmed = link.trim();
|
|
2577
2605
|
if (!trimmed) return;
|
|
@@ -2661,7 +2689,7 @@ function SearchTab({ port, onPick }) {
|
|
|
2661
2689
|
const [page, setPage] = useState2(null);
|
|
2662
2690
|
const [busy, setBusy] = useState2(false);
|
|
2663
2691
|
const [error, setError] = useState2(null);
|
|
2664
|
-
const seq =
|
|
2692
|
+
const seq = useRef3(0);
|
|
2665
2693
|
const run = async (cursor) => {
|
|
2666
2694
|
if (!port.search) return;
|
|
2667
2695
|
const mine = ++seq.current;
|
|
@@ -2677,7 +2705,7 @@ function SearchTab({ port, onPick }) {
|
|
|
2677
2705
|
if (mine === seq.current) setBusy(false);
|
|
2678
2706
|
}
|
|
2679
2707
|
};
|
|
2680
|
-
|
|
2708
|
+
useEffect4(() => {
|
|
2681
2709
|
if (port.search) void run(null);
|
|
2682
2710
|
}, [kind, sort]);
|
|
2683
2711
|
if (!port.search) return /* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground", children: "Search is not available here yet. Paste a link instead." });
|
|
@@ -2814,8 +2842,10 @@ export {
|
|
|
2814
2842
|
useLocalModels,
|
|
2815
2843
|
useModelAccess,
|
|
2816
2844
|
useModelCatalog,
|
|
2845
|
+
useModelCatalogRevision,
|
|
2817
2846
|
useModelCatalogStore,
|
|
2818
2847
|
useModelCatalogUiPorts,
|
|
2819
2848
|
useModelFavorites,
|
|
2820
|
-
useModelRecordsSelector
|
|
2849
|
+
useModelRecordsSelector,
|
|
2850
|
+
useOnModelCatalogChanged
|
|
2821
2851
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/agents",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.258",
|
|
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",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
|
+
"scripts/check-model-catalog-reads.mjs",
|
|
27
28
|
"README.md",
|
|
28
29
|
"LICENSE",
|
|
29
30
|
"CHANGELOG.md"
|
|
@@ -509,11 +510,11 @@
|
|
|
509
510
|
"sideEffects": false,
|
|
510
511
|
"dependencies": {
|
|
511
512
|
"@ai-matrx/alchemy": "latest",
|
|
513
|
+
"@ai-matrx/api": "latest",
|
|
512
514
|
"@ai-matrx/associations": "latest",
|
|
513
515
|
"@ai-matrx/content-ir": "latest",
|
|
514
|
-
"@ai-matrx/api": "latest",
|
|
515
|
-
"@ai-matrx/design-system": "latest",
|
|
516
516
|
"@ai-matrx/data": "latest",
|
|
517
|
+
"@ai-matrx/design-system": "latest",
|
|
517
518
|
"@ai-matrx/diff": "latest",
|
|
518
519
|
"@ai-matrx/icons": "latest",
|
|
519
520
|
"@ai-matrx/kit": "latest"
|
|
@@ -555,11 +556,11 @@
|
|
|
555
556
|
"scripts": {
|
|
556
557
|
"build": "pnpm clean && node ./scripts/build-parts.mjs && node ../scripts/guard-exit.mjs ../scripts/check-node-env-not-inlined.mjs",
|
|
557
558
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
558
|
-
"check:package": "node ./scripts/check-catalog-reader-columns.mjs && node ./scripts/check-catalog-reader-columns.mjs --self-test && node ../scripts/check-no-model-literals.mjs && node ../scripts/check-no-model-literals.mjs --self-test && node ../scripts/check-agents-below-chat.mjs && node ../scripts/check-agents-below-chat.mjs --self-test && pnpm build && node ../scripts/check-exports-agreement.mjs && publint && pnpm verify:tarball && node ../scripts/check-tarball-relative-imports.mjs --self-test && node ../scripts/check-tarball-relative-imports.mjs --pack . && node ../scripts/check-package-rules.mjs --self-test && node ../scripts/check-package-rules.mjs .",
|
|
559
|
+
"check:package": "node ./scripts/check-model-catalog-reads.mjs && node ./scripts/check-model-catalog-reads.mjs --self-test && node ./scripts/check-catalog-reader-columns.mjs && node ./scripts/check-catalog-reader-columns.mjs --self-test && node ../scripts/check-no-model-literals.mjs && node ../scripts/check-no-model-literals.mjs --self-test && node ../scripts/check-agents-below-chat.mjs && node ../scripts/check-agents-below-chat.mjs --self-test && pnpm build && node ../scripts/check-exports-agreement.mjs && publint && pnpm verify:tarball && node ../scripts/check-tarball-relative-imports.mjs --self-test && node ../scripts/check-tarball-relative-imports.mjs --pack . && node ../scripts/check-package-rules.mjs --self-test && node ../scripts/check-package-rules.mjs .",
|
|
559
560
|
"typecheck": "tsc --noEmit",
|
|
560
561
|
"test": "vitest run",
|
|
561
562
|
"verify:tarball": "node ./scripts/verify-tarball.mjs",
|
|
562
563
|
"typegen:self-test": "node scripts/typegen/drop-guard.self-test.mjs && node scripts/typegen/check-api-types-fresh.mjs --self-test"
|
|
563
564
|
},
|
|
564
|
-
"gitHead": "
|
|
565
|
+
"gitHead": "48dc0c53e766ad507fa37f41893b2b358be229f4"
|
|
565
566
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ONE MODEL CATALOG READER (owner ruling 2026-10-23). Every surface that names or lists a model reads the
|
|
4
|
+
* model catalog through `@ai-matrx/agents/models` (records / readModelCatalog, subscribed to
|
|
5
|
+
* `catalog_changed` in models/catalog/changes.ts) and computes the name with `modelDisplayLabel`
|
|
6
|
+
* (models/label.ts), so an admin edit reaches every open app within seconds.
|
|
7
|
+
*
|
|
8
|
+
* Fails when a file outside apps/shared/matrx-agents/models/:
|
|
9
|
+
* R1 reads ai.model_definition / model_public / model_offering / model_config / model_variant /
|
|
10
|
+
* model_presentation (`.from("…")`) or the catalog RPCs (`admin_model_catalog`, `resolve_model_config`);
|
|
11
|
+
* R2 computes a model label by hand (`x.common_name || x.name`, `?? …name`) instead of `modelDisplayLabel`.
|
|
12
|
+
* ALLOWED lists the few readers that keep their own read on purpose, each with why and how it follows the
|
|
13
|
+
* event. Test files are fixtures and are not scanned.
|
|
14
|
+
*
|
|
15
|
+
* node apps/shared/matrx-agents/scripts/check-model-catalog-reads.mjs [--root <repo>] [--extra <dir>]... [--self-test]
|
|
16
|
+
* `--extra` adds a scan root (the red proof plants an untracked scratch file there). `--scan` replaces the
|
|
17
|
+
* default roots (Matrx 2: `node node_modules/@ai-matrx/agents/scripts/check-model-catalog-reads.mjs --root . --scan app`).
|
|
18
|
+
*/
|
|
19
|
+
import { mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
import { join, relative, resolve } from "node:path";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
|
+
|
|
24
|
+
const here = fileURLToPath(new URL(".", import.meta.url));
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
const flag = (name) => {
|
|
27
|
+
const out = [];
|
|
28
|
+
for (let i = 0; i < args.length; i++) if (args[i] === name && args[i + 1]) out.push(args[++i]);
|
|
29
|
+
return out;
|
|
30
|
+
};
|
|
31
|
+
const ROOT = resolve(flag("--root")[0] ?? join(here, "../../../.."));
|
|
32
|
+
// `--scan <dir>` replaces the aidream roots: Matrx 2 runs this file from its installed
|
|
33
|
+
// node_modules/@ai-matrx/agents with `--root . --scan app` (one guard, two repos).
|
|
34
|
+
const ROOTS = [...(flag("--scan").length ? flag("--scan") : ["apps/shared", "apps/matrx-frontend"]), ...flag("--extra")];
|
|
35
|
+
|
|
36
|
+
/** The reader module: the only place the catalog relations are read and the label is computed. */
|
|
37
|
+
const HOME = "apps/shared/matrx-agents/models/";
|
|
38
|
+
|
|
39
|
+
/** Files that keep their own catalog read, on purpose. Each says why and how it follows catalog_changed. */
|
|
40
|
+
export const ALLOWED = {
|
|
41
|
+
// The admin model EDITOR: reads and writes every column of the rows it edits. Every container that shows
|
|
42
|
+
// its rows re-reads on the event (useOnModelCatalogChanged / useModelCatalogRevision).
|
|
43
|
+
"apps/matrx-frontend/features/ai-models/service.ts": "admin editor CRUD; containers follow catalog_changed",
|
|
44
|
+
"apps/matrx-frontend/features/ai-models/discovery/api.ts": "admin discovery CRUD; UnifiedModelKindTable follows catalog_changed",
|
|
45
|
+
"apps/matrx-frontend/features/ai-models/translation/data.ts": "admin translation grid; settings-translation keys its query on the revision",
|
|
46
|
+
// cx-dashboard names models on the server per request; CxDashboardLayoutClient router.refresh()es on the event.
|
|
47
|
+
"apps/matrx-frontend/features/cx-dashboard/service.ts": "server-rendered admin page; layout refreshes on catalog_changed",
|
|
48
|
+
// The public JSON API for external callers (anon key, CDN cached by design) — not a screen.
|
|
49
|
+
"apps/matrx-frontend/app/api/ai-models/route.ts": "public CDN-cached JSON API for external callers, not a screen",
|
|
50
|
+
// A security probe that reads the relations to prove their grants.
|
|
51
|
+
"apps/matrx-frontend/scripts/check-rls-on.ts": "RLS probe, not a reader",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const SKIP_DIR = new Set(["node_modules", "dist", ".next", ".turbo", "coverage", "generated", "__tests__", "__fixtures__"]);
|
|
55
|
+
const isTest = (p) => /\.(test|spec)\.[cm]?[jt]sx?$/.test(p) || /\.d\.ts$/.test(p) || /\.generated\./.test(p);
|
|
56
|
+
|
|
57
|
+
const R1 = /\.from\(\s*["'`](model_definition|model_public|model_offering|model_config|model_variant|model_presentation)["'`]|\.rpc\(\s*["'`](admin_model_catalog|resolve_model_config)["'`]/g;
|
|
58
|
+
const R2 = /\bcommon_name\s*(\|\||\?\?)\s*[\w$?.]*\bname\b/g;
|
|
59
|
+
|
|
60
|
+
function* walk(dir) {
|
|
61
|
+
let names;
|
|
62
|
+
try {
|
|
63
|
+
names = readdirSync(dir);
|
|
64
|
+
} catch {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
for (const n of names) {
|
|
68
|
+
if (SKIP_DIR.has(n)) continue;
|
|
69
|
+
const p = join(dir, n);
|
|
70
|
+
const st = statSync(p);
|
|
71
|
+
if (st.isDirectory()) yield* walk(p);
|
|
72
|
+
else if (/\.(ts|tsx|mts|cts|js|jsx|mjs)$/.test(n) && !isTest(n)) yield p;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const lineOf = (src, index) => src.slice(0, index).split("\n").length;
|
|
77
|
+
function scan(roots) {
|
|
78
|
+
const findings = [];
|
|
79
|
+
for (const r of roots) {
|
|
80
|
+
for (const file of walk(resolve(ROOT, r))) {
|
|
81
|
+
const rel = relative(ROOT, file).split("\\").join("/");
|
|
82
|
+
if (rel.startsWith(HOME) || rel === "apps/shared/matrx-agents/scripts/check-model-catalog-reads.mjs") continue;
|
|
83
|
+
if (ALLOWED[rel]) continue;
|
|
84
|
+
const src = readFileSync(file, "utf8");
|
|
85
|
+
for (const [rule, re, what] of [
|
|
86
|
+
["R1", R1, "reads the model catalog directly — use @ai-matrx/agents/models (records / chat's useModelLabel / readModelIdentities)"],
|
|
87
|
+
["R2", R2, "computes a model label by hand — use modelDisplayLabel(row) from @ai-matrx/agents/models"],
|
|
88
|
+
]) {
|
|
89
|
+
re.lastIndex = 0;
|
|
90
|
+
for (let m; (m = re.exec(src)); ) findings.push(`${rel}:${lineOf(src, m.index)} ${rule} ${what} [${m[0].replace(/\s+/g, " ")}]`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return findings;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (args.includes("--self-test")) {
|
|
98
|
+
// Falsifiability: a planted direct read and a hand-made label must each fail; the lawful form must pass.
|
|
99
|
+
const dir = mkdtempSync(join(tmpdir(), "model-catalog-reads-"));
|
|
100
|
+
try {
|
|
101
|
+
writeFileSync(join(dir, "bad.ts"), 'export const r = (db: any) => db.schema("ai").from("model_definition").select("id");\nexport const l = (m: any) =>\n m?.common_name ??\n m?.name;\n');
|
|
102
|
+
writeFileSync(join(dir, "good.ts"), 'import { modelDisplayLabel } from "@ai-matrx/agents/models";\nexport const l = (m: any) => modelDisplayLabel(m) ?? m.common_name ?? null;\n');
|
|
103
|
+
const got = scan([dir]);
|
|
104
|
+
const ok = got.length === 2 && got.some((f) => f.includes("bad.ts:1 R1")) && got.some((f) => f.includes("bad.ts:3 R2"));
|
|
105
|
+
if (!ok) {
|
|
106
|
+
console.error(`check-model-catalog-reads --self-test: FAILED — expected R1+R2 on bad.ts only, got:\n ${got.join("\n ") || "(nothing)"}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
console.log("check-model-catalog-reads --self-test: OK — a planted read and a hand-made label both go red; modelDisplayLabel passes.");
|
|
110
|
+
process.exit(0);
|
|
111
|
+
} finally {
|
|
112
|
+
rmSync(dir, { recursive: true, force: true });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const findings = scan(ROOTS);
|
|
117
|
+
if (findings.length) {
|
|
118
|
+
console.error(`check-model-catalog-reads: ${findings.length} finding(s) — every model name and list goes through @ai-matrx/agents/models so catalog_changed reaches it:`);
|
|
119
|
+
for (const f of findings) console.error(` ${f}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
console.log(`check-model-catalog-reads: OK — no model catalog read or hand-made model label outside ${HOME} (${Object.keys(ALLOWED).length} allowed readers).`);
|