@shop-os/modelkit 0.1.11 → 0.2.0
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 +24 -3
- package/dist/catalog.cjs +395 -0
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +41 -1
- package/dist/catalog.d.ts +41 -1
- package/dist/catalog.js +395 -0
- package/dist/catalog.js.map +1 -1
- package/dist/index.cjs +539 -116
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +532 -116
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,7 +9,27 @@ header in the release PR.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
-
## [0.
|
|
12
|
+
## [0.2.0] - 2026-09-14
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- `CatalogModelSchema` and `CatalogSchema` (`src/catalog/schema.ts`): Zod schema mirroring
|
|
16
|
+
`CatalogModel` field for field, tolerating unknown keys. Active rows need `display`, active
|
|
17
|
+
text rows need `capabilities.contextWindow`, `replacedBy` must not equal `id`, and ids and
|
|
18
|
+
aliases stay globally unique. `MODELS` passes it.
|
|
19
|
+
- `liveCatalog` (`src/catalog/live.ts`): the shared live catalog, starting as `MODELS`.
|
|
20
|
+
- `primeCatalog` and `resetLiveCatalog` (`src/catalog/load.ts`): fetch, validate and swap the
|
|
21
|
+
live catalog from `GET /models/catalog`, with `If-None-Match`, a `CatalogCache` last-good
|
|
22
|
+
fallback, the `MODELS` snapshot fallback, and a background refresh every `ttlMs`. Never
|
|
23
|
+
throws. Exported from both the node entry and `@shop-os/modelkit/catalog`.
|
|
24
|
+
- `fileCatalogCache` (`src/catalog/file-cache.ts`, node entry only): file-backed last-good
|
|
25
|
+
store, written atomically via `path + ".tmp"` plus rename.
|
|
26
|
+
- `Catalog.replace(rows)`: atomic swap of the id and alias maps.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- `createModelKit` reads `liveCatalog` when no `catalogOverride` is given, so a primed catalog
|
|
30
|
+
reaches an existing kit without a rebuild.
|
|
31
|
+
|
|
32
|
+
## [0.1.11] - 2026-09-13
|
|
13
33
|
|
|
14
34
|
### Added
|
|
15
35
|
- `display.logo` on every catalog row except `mmaudio` and `bg-remove`: text rows carry the
|
|
@@ -189,8 +209,9 @@ First publish, renamed from `@shop-os/model-sdk`.
|
|
|
189
209
|
- Keys per tier (pro, enterprise with per-provider fallback); one retry classifier, one error
|
|
190
210
|
type, one usage event with the canonical id.
|
|
191
211
|
|
|
192
|
-
[Unreleased]: https://github.com/Shop-OS/shopos-backend-ts/compare/modelkit-0.
|
|
193
|
-
[0.
|
|
212
|
+
[Unreleased]: https://github.com/Shop-OS/shopos-backend-ts/compare/modelkit-0.2.0...dev
|
|
213
|
+
[0.2.0]: https://github.com/Shop-OS/shopos-backend-ts/compare/modelkit-0.1.11...modelkit-0.2.0
|
|
214
|
+
[0.1.11]: https://github.com/Shop-OS/shopos-backend-ts/compare/f0981d9b9...bb3b16354
|
|
194
215
|
[0.1.10]: https://github.com/Shop-OS/shopos-backend-ts/compare/f17e822e2...f0981d9b9
|
|
195
216
|
[0.1.9]: https://github.com/Shop-OS/shopos-backend-ts/compare/1958a5615...dev
|
|
196
217
|
[0.1.8]: https://github.com/Shop-OS/shopos-backend-ts/commit/9661a109d
|
package/dist/catalog.cjs
CHANGED
|
@@ -20,7 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/catalog-entry.ts
|
|
21
21
|
var catalog_entry_exports = {};
|
|
22
22
|
__export(catalog_entry_exports, {
|
|
23
|
+
ADAPTER_IDS: () => ADAPTER_IDS,
|
|
23
24
|
Catalog: () => Catalog,
|
|
25
|
+
CatalogModelSchema: () => CatalogModelSchema,
|
|
26
|
+
CatalogSchema: () => CatalogSchema,
|
|
24
27
|
IMAGE_MODELS: () => IMAGE_MODELS,
|
|
25
28
|
MODELS: () => MODELS,
|
|
26
29
|
ModelKitError: () => ModelKitError,
|
|
@@ -28,7 +31,10 @@ __export(catalog_entry_exports, {
|
|
|
28
31
|
TEXT_MODELS: () => TEXT_MODELS,
|
|
29
32
|
VIDEO_MODELS: () => VIDEO_MODELS,
|
|
30
33
|
isModelKitError: () => isModelKitError,
|
|
34
|
+
liveCatalog: () => liveCatalog,
|
|
31
35
|
nearestAspectRatio: () => nearestAspectRatio,
|
|
36
|
+
primeCatalog: () => primeCatalog,
|
|
37
|
+
resetLiveCatalog: () => resetLiveCatalog,
|
|
32
38
|
resolveAudioOptions: () => resolveAudioOptions,
|
|
33
39
|
resolveImageOptions: () => resolveImageOptions,
|
|
34
40
|
resolveOptions: () => resolveOptions,
|
|
@@ -131,6 +137,14 @@ var Catalog = class _Catalog {
|
|
|
131
137
|
...rows
|
|
132
138
|
]);
|
|
133
139
|
}
|
|
140
|
+
/** Atomically swap every row. Builds the maps first so a bad batch throws before anything changes. */
|
|
141
|
+
replace(rows) {
|
|
142
|
+
const next = new _Catalog(rows);
|
|
143
|
+
this.byId.clear();
|
|
144
|
+
this.byAlias.clear();
|
|
145
|
+
for (const [id, model] of next.byId) this.byId.set(id, model);
|
|
146
|
+
for (const [alias, id] of next.byAlias) this.byAlias.set(alias, id);
|
|
147
|
+
}
|
|
134
148
|
};
|
|
135
149
|
|
|
136
150
|
// src/catalog/image.ts
|
|
@@ -1821,6 +1835,387 @@ var MODELS = [
|
|
|
1821
1835
|
...OTHER_MODELS
|
|
1822
1836
|
];
|
|
1823
1837
|
|
|
1838
|
+
// src/catalog/schema.ts
|
|
1839
|
+
var import_zod = require("zod");
|
|
1840
|
+
var ADAPTER_IDS = [
|
|
1841
|
+
"ai-sdk-text",
|
|
1842
|
+
"google-image",
|
|
1843
|
+
"openai-image",
|
|
1844
|
+
"google-genai",
|
|
1845
|
+
"ark",
|
|
1846
|
+
"fal",
|
|
1847
|
+
"replicate",
|
|
1848
|
+
"prodia",
|
|
1849
|
+
"higgsfield",
|
|
1850
|
+
"xai-video"
|
|
1851
|
+
];
|
|
1852
|
+
var ModelTypeSchema = import_zod.z.enum(["text", "image", "video", "audio", "upscale", "bg-remove"]);
|
|
1853
|
+
var LifecycleSchema = import_zod.z.enum(["active", "deprecated", "retired"]);
|
|
1854
|
+
var AdapterSchema = import_zod.z.enum(ADAPTER_IDS);
|
|
1855
|
+
var AspectRatioSchema = import_zod.z.enum([
|
|
1856
|
+
"1:1",
|
|
1857
|
+
"16:9",
|
|
1858
|
+
"9:16",
|
|
1859
|
+
"4:3",
|
|
1860
|
+
"3:4",
|
|
1861
|
+
"3:2",
|
|
1862
|
+
"2:3",
|
|
1863
|
+
"4:5",
|
|
1864
|
+
"5:4",
|
|
1865
|
+
"21:9",
|
|
1866
|
+
"9:21",
|
|
1867
|
+
"97:30",
|
|
1868
|
+
"adaptive"
|
|
1869
|
+
]);
|
|
1870
|
+
var ImageSizeSchema = import_zod.z.enum(["512", "1K", "2K", "4K"]);
|
|
1871
|
+
var VideoResolutionSchema = import_zod.z.enum(["360p", "480p", "720p", "768p", "1080p", "2k", "4k"]);
|
|
1872
|
+
var ImageQualitySchema = import_zod.z.enum(["auto", "low", "medium", "high", "xhigh", "max"]);
|
|
1873
|
+
var OutputFormatSchema = import_zod.z.enum(["png", "jpeg", "webp"]);
|
|
1874
|
+
var RequiredInputSchema = import_zod.z.enum(["firstFrame", "sourceVideo", "references"]);
|
|
1875
|
+
var SupportedOptionSchema = import_zod.z.enum([
|
|
1876
|
+
"seed",
|
|
1877
|
+
"quality",
|
|
1878
|
+
"negativePrompt",
|
|
1879
|
+
"cfgScale",
|
|
1880
|
+
"generateAudio",
|
|
1881
|
+
"promptExpansion",
|
|
1882
|
+
"lastFrame",
|
|
1883
|
+
"count",
|
|
1884
|
+
"outputFormat",
|
|
1885
|
+
"scaleFactor",
|
|
1886
|
+
"safetySettings",
|
|
1887
|
+
"temperature",
|
|
1888
|
+
"upscale",
|
|
1889
|
+
"watermark",
|
|
1890
|
+
"safetyChecker",
|
|
1891
|
+
"instrumental"
|
|
1892
|
+
]);
|
|
1893
|
+
var UsageUnitSchema = import_zod.z.enum([
|
|
1894
|
+
"tokens",
|
|
1895
|
+
"images",
|
|
1896
|
+
"seconds",
|
|
1897
|
+
"reported_cost_usd",
|
|
1898
|
+
"compute_seconds"
|
|
1899
|
+
]);
|
|
1900
|
+
var PricingKindSchema = import_zod.z.enum(["token", "image_size", "video_per_second", "flat"]);
|
|
1901
|
+
var ProviderOptionValueSchema = import_zod.z.lazy(
|
|
1902
|
+
() => import_zod.z.union([
|
|
1903
|
+
import_zod.z.string(),
|
|
1904
|
+
import_zod.z.number(),
|
|
1905
|
+
import_zod.z.boolean(),
|
|
1906
|
+
import_zod.z.array(ProviderOptionValueSchema),
|
|
1907
|
+
import_zod.z.record(import_zod.z.string(), ProviderOptionValueSchema)
|
|
1908
|
+
])
|
|
1909
|
+
);
|
|
1910
|
+
var ProviderOptionsSchema = import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), ProviderOptionValueSchema));
|
|
1911
|
+
var ReferenceLimitsSchema = import_zod.z.looseObject({
|
|
1912
|
+
images: import_zod.z.number().optional(),
|
|
1913
|
+
videos: import_zod.z.number().optional(),
|
|
1914
|
+
audios: import_zod.z.number().optional(),
|
|
1915
|
+
total: import_zod.z.number().optional(),
|
|
1916
|
+
audioOnly: import_zod.z.boolean().optional()
|
|
1917
|
+
});
|
|
1918
|
+
var ModelConstraintsSchema = import_zod.z.looseObject({
|
|
1919
|
+
aspectRatios: import_zod.z.array(AspectRatioSchema).optional(),
|
|
1920
|
+
sizes: import_zod.z.array(ImageSizeSchema).optional(),
|
|
1921
|
+
resolutions: import_zod.z.array(VideoResolutionSchema).optional(),
|
|
1922
|
+
durations: import_zod.z.union([import_zod.z.looseObject({ min: import_zod.z.number(), max: import_zod.z.number() }), import_zod.z.array(import_zod.z.number())]).optional(),
|
|
1923
|
+
durationPresets: import_zod.z.array(import_zod.z.number()).optional(),
|
|
1924
|
+
qualityOptions: import_zod.z.array(import_zod.z.string()).optional(),
|
|
1925
|
+
exactSizes: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
|
|
1926
|
+
references: ReferenceLimitsSchema.optional(),
|
|
1927
|
+
requires: import_zod.z.array(RequiredInputSchema).optional(),
|
|
1928
|
+
supports: import_zod.z.array(SupportedOptionSchema).optional(),
|
|
1929
|
+
ratioIgnoredWith: import_zod.z.array(import_zod.z.enum(["firstFrame", "sourceVideo"])).optional()
|
|
1930
|
+
});
|
|
1931
|
+
var MediaRefSchema = import_zod.z.union([
|
|
1932
|
+
import_zod.z.looseObject({ url: import_zod.z.string(), label: import_zod.z.string().optional() }),
|
|
1933
|
+
import_zod.z.looseObject({
|
|
1934
|
+
bytes: import_zod.z.instanceof(Uint8Array),
|
|
1935
|
+
mime: import_zod.z.string(),
|
|
1936
|
+
label: import_zod.z.string().optional()
|
|
1937
|
+
})
|
|
1938
|
+
]);
|
|
1939
|
+
var ImageSafetySettingSchema = import_zod.z.looseObject({ category: import_zod.z.string(), threshold: import_zod.z.string() });
|
|
1940
|
+
var UpscaleOptionsSchema = import_zod.z.looseObject({
|
|
1941
|
+
enhanceModel: import_zod.z.string().optional(),
|
|
1942
|
+
subjectDetection: import_zod.z.string().optional(),
|
|
1943
|
+
faceEnhancement: import_zod.z.boolean().optional(),
|
|
1944
|
+
faceEnhancementCreativity: import_zod.z.number().optional(),
|
|
1945
|
+
faceEnhancementStrength: import_zod.z.number().optional()
|
|
1946
|
+
});
|
|
1947
|
+
var DefaultsSchema = import_zod.z.looseObject({
|
|
1948
|
+
references: import_zod.z.array(MediaRefSchema).optional(),
|
|
1949
|
+
aspectRatio: AspectRatioSchema.optional(),
|
|
1950
|
+
size: ImageSizeSchema.optional(),
|
|
1951
|
+
exactSize: import_zod.z.string().optional(),
|
|
1952
|
+
quality: ImageQualitySchema.optional(),
|
|
1953
|
+
seed: import_zod.z.number().optional(),
|
|
1954
|
+
count: import_zod.z.number().optional(),
|
|
1955
|
+
outputFormat: OutputFormatSchema.optional(),
|
|
1956
|
+
scaleFactor: import_zod.z.number().optional(),
|
|
1957
|
+
safetySettings: import_zod.z.array(ImageSafetySettingSchema).optional(),
|
|
1958
|
+
temperature: import_zod.z.number().optional(),
|
|
1959
|
+
upscale: UpscaleOptionsSchema.optional(),
|
|
1960
|
+
firstFrame: MediaRefSchema.optional(),
|
|
1961
|
+
lastFrame: MediaRefSchema.optional(),
|
|
1962
|
+
sourceVideo: MediaRefSchema.optional(),
|
|
1963
|
+
referenceImages: import_zod.z.array(MediaRefSchema).optional(),
|
|
1964
|
+
referenceVideos: import_zod.z.array(MediaRefSchema).optional(),
|
|
1965
|
+
referenceAudios: import_zod.z.array(MediaRefSchema).optional(),
|
|
1966
|
+
resolution: VideoResolutionSchema.optional(),
|
|
1967
|
+
durationSeconds: import_zod.z.number().optional(),
|
|
1968
|
+
generateAudio: import_zod.z.boolean().optional(),
|
|
1969
|
+
negativePrompt: import_zod.z.string().optional(),
|
|
1970
|
+
cfgScale: import_zod.z.number().optional(),
|
|
1971
|
+
promptExpansion: import_zod.z.enum(["fast", "balanced", "quality"]).optional(),
|
|
1972
|
+
watermark: import_zod.z.boolean().optional(),
|
|
1973
|
+
safetyChecker: import_zod.z.boolean().optional(),
|
|
1974
|
+
mode: import_zod.z.enum(["standard", "pro"]).optional(),
|
|
1975
|
+
instrumental: import_zod.z.boolean().optional()
|
|
1976
|
+
});
|
|
1977
|
+
var CatalogDisplaySchema = import_zod.z.looseObject({
|
|
1978
|
+
provider: import_zod.z.string(),
|
|
1979
|
+
description: import_zod.z.string(),
|
|
1980
|
+
logo: import_zod.z.string().optional(),
|
|
1981
|
+
costTier: import_zod.z.union([import_zod.z.literal(1), import_zod.z.literal(2), import_zod.z.literal(3)]).optional(),
|
|
1982
|
+
recommended: import_zod.z.boolean().optional(),
|
|
1983
|
+
sortOrder: import_zod.z.number().optional(),
|
|
1984
|
+
chat: import_zod.z.looseObject({
|
|
1985
|
+
providerOptions: ProviderOptionsSchema.optional(),
|
|
1986
|
+
temperature: import_zod.z.number().optional()
|
|
1987
|
+
}).optional()
|
|
1988
|
+
});
|
|
1989
|
+
var CatalogCapabilitiesSchema = import_zod.z.looseObject({
|
|
1990
|
+
acceptsSourceVideo: import_zod.z.boolean().optional(),
|
|
1991
|
+
framesExcludeReferences: import_zod.z.boolean().optional(),
|
|
1992
|
+
reasoning: import_zod.z.boolean().optional(),
|
|
1993
|
+
vision: import_zod.z.boolean().optional(),
|
|
1994
|
+
contextWindow: import_zod.z.number().optional()
|
|
1995
|
+
});
|
|
1996
|
+
var CatalogModelSchemaBase = import_zod.z.looseObject({
|
|
1997
|
+
id: import_zod.z.string().min(1),
|
|
1998
|
+
aliases: import_zod.z.array(import_zod.z.string()).default([]),
|
|
1999
|
+
label: import_zod.z.string().min(1),
|
|
2000
|
+
type: ModelTypeSchema,
|
|
2001
|
+
adapter: AdapterSchema,
|
|
2002
|
+
providerModelId: import_zod.z.string().min(1),
|
|
2003
|
+
lifecycle: LifecycleSchema,
|
|
2004
|
+
replacedBy: import_zod.z.string().optional(),
|
|
2005
|
+
exposure: import_zod.z.enum(["catalog", "internal"]),
|
|
2006
|
+
constraints: ModelConstraintsSchema,
|
|
2007
|
+
defaults: DefaultsSchema,
|
|
2008
|
+
providerOptions: ProviderOptionsSchema.optional(),
|
|
2009
|
+
usage: import_zod.z.looseObject({
|
|
2010
|
+
unit: UsageUnitSchema,
|
|
2011
|
+
slug: import_zod.z.string().optional(),
|
|
2012
|
+
providerLabel: import_zod.z.string().optional()
|
|
2013
|
+
}),
|
|
2014
|
+
pricingKind: PricingKindSchema,
|
|
2015
|
+
display: CatalogDisplaySchema.optional(),
|
|
2016
|
+
capabilities: CatalogCapabilitiesSchema.optional()
|
|
2017
|
+
}).superRefine((row, ctx) => {
|
|
2018
|
+
if (row.lifecycle === "active" && !row.display) {
|
|
2019
|
+
ctx.addIssue({
|
|
2020
|
+
code: "custom",
|
|
2021
|
+
path: ["display"],
|
|
2022
|
+
message: "active rows need display"
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
if (row.lifecycle === "active" && row.type === "text" && !row.capabilities?.contextWindow) {
|
|
2026
|
+
ctx.addIssue({
|
|
2027
|
+
code: "custom",
|
|
2028
|
+
path: ["capabilities", "contextWindow"],
|
|
2029
|
+
message: "active text rows need contextWindow"
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
if (row.replacedBy !== void 0 && row.replacedBy === row.id) {
|
|
2033
|
+
ctx.addIssue({
|
|
2034
|
+
code: "custom",
|
|
2035
|
+
path: ["replacedBy"],
|
|
2036
|
+
message: "replacedBy must not equal id"
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
});
|
|
2040
|
+
var CatalogModelSchema = CatalogModelSchemaBase;
|
|
2041
|
+
var CatalogSchema = import_zod.z.array(CatalogModelSchemaBase).superRefine((rows, ctx) => {
|
|
2042
|
+
const seenIds = /* @__PURE__ */ new Map();
|
|
2043
|
+
rows.forEach((row, index) => {
|
|
2044
|
+
if (seenIds.has(row.id)) {
|
|
2045
|
+
ctx.addIssue({
|
|
2046
|
+
code: "custom",
|
|
2047
|
+
path: [index, "id"],
|
|
2048
|
+
message: `Duplicate model id "${row.id}"`
|
|
2049
|
+
});
|
|
2050
|
+
} else {
|
|
2051
|
+
seenIds.set(row.id, index);
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
const seenAliases = /* @__PURE__ */ new Map();
|
|
2055
|
+
rows.forEach((row, index) => {
|
|
2056
|
+
row.aliases.forEach((alias, aliasIndex) => {
|
|
2057
|
+
const path = [index, "aliases", aliasIndex];
|
|
2058
|
+
if (alias === row.id || seenIds.has(alias)) {
|
|
2059
|
+
ctx.addIssue({
|
|
2060
|
+
code: "custom",
|
|
2061
|
+
path,
|
|
2062
|
+
message: `Alias "${alias}" collides with a model id`
|
|
2063
|
+
});
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
if (seenAliases.has(alias)) {
|
|
2067
|
+
ctx.addIssue({
|
|
2068
|
+
code: "custom",
|
|
2069
|
+
path,
|
|
2070
|
+
message: `Duplicate alias "${alias}"`
|
|
2071
|
+
});
|
|
2072
|
+
} else {
|
|
2073
|
+
seenAliases.set(alias, index);
|
|
2074
|
+
}
|
|
2075
|
+
});
|
|
2076
|
+
});
|
|
2077
|
+
});
|
|
2078
|
+
|
|
2079
|
+
// src/catalog/live.ts
|
|
2080
|
+
var liveCatalog = new Catalog(MODELS);
|
|
2081
|
+
|
|
2082
|
+
// src/catalog/load.ts
|
|
2083
|
+
var memo;
|
|
2084
|
+
var timer;
|
|
2085
|
+
var revision;
|
|
2086
|
+
var origin = "snapshot";
|
|
2087
|
+
function rowCount() {
|
|
2088
|
+
return liveCatalog.list({ lifecycle: ["active", "deprecated", "retired"] }).length;
|
|
2089
|
+
}
|
|
2090
|
+
function authHeaders(auth) {
|
|
2091
|
+
return "bearer" in auth ? { Authorization: `Bearer ${auth.bearer}` } : { "x-api-key": auth.apiKey };
|
|
2092
|
+
}
|
|
2093
|
+
function describeError(err) {
|
|
2094
|
+
return err instanceof Error ? err.message : String(err);
|
|
2095
|
+
}
|
|
2096
|
+
function parseEnvelope(json) {
|
|
2097
|
+
if (typeof json !== "object" || json === null || !("rows" in json)) {
|
|
2098
|
+
throw new Error("invalid catalog response: missing rows");
|
|
2099
|
+
}
|
|
2100
|
+
const rows = CatalogSchema.parse(json.rows);
|
|
2101
|
+
const rev = json.revision;
|
|
2102
|
+
if (typeof rev === "string") return { rows, revision: rev };
|
|
2103
|
+
return { rows };
|
|
2104
|
+
}
|
|
2105
|
+
function logPrimed(logger, result) {
|
|
2106
|
+
const { origin: loggedOrigin, rowCount: loggedCount, revision: loggedRevision } = result;
|
|
2107
|
+
logger.info("[modelkit] catalog primed", {
|
|
2108
|
+
origin: loggedOrigin,
|
|
2109
|
+
rowCount: loggedCount,
|
|
2110
|
+
revision: loggedRevision
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
async function fetchRemote(source, logger) {
|
|
2114
|
+
const fetchImpl = source.fetch ?? globalThis.fetch;
|
|
2115
|
+
const headers = authHeaders(source.auth);
|
|
2116
|
+
if (revision !== void 0) headers["If-None-Match"] = `"${revision}"`;
|
|
2117
|
+
const res = await fetchImpl(`${source.baseUrl}/models/catalog`, {
|
|
2118
|
+
headers,
|
|
2119
|
+
signal: AbortSignal.timeout(source.timeoutMs ?? 1e4)
|
|
2120
|
+
});
|
|
2121
|
+
if (res.status === 304) return { kept: true };
|
|
2122
|
+
if (!res.ok) throw new Error(`catalog fetch failed with status ${res.status}`);
|
|
2123
|
+
const json = await res.json();
|
|
2124
|
+
const { rows, revision: rev } = parseEnvelope(json);
|
|
2125
|
+
if (rev === void 0) return { rows, fromCache: false };
|
|
2126
|
+
return { rows, revision: rev, fromCache: false };
|
|
2127
|
+
}
|
|
2128
|
+
async function writeCache(source, logger, rows, rev) {
|
|
2129
|
+
if (!source.cache) return;
|
|
2130
|
+
try {
|
|
2131
|
+
await source.cache.set(JSON.stringify({ rows, revision: rev }));
|
|
2132
|
+
} catch (err) {
|
|
2133
|
+
logger.warn("[modelkit] catalog cache write failed", err);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
async function prime(source, logger) {
|
|
2137
|
+
try {
|
|
2138
|
+
const fetched = await fetchRemote(source, logger);
|
|
2139
|
+
if ("kept" in fetched) {
|
|
2140
|
+
const result2 = { origin, rowCount: rowCount() };
|
|
2141
|
+
if (revision !== void 0) result2.revision = revision;
|
|
2142
|
+
logPrimed(logger, result2);
|
|
2143
|
+
return result2;
|
|
2144
|
+
}
|
|
2145
|
+
liveCatalog.replace(fetched.rows);
|
|
2146
|
+
origin = "remote";
|
|
2147
|
+
revision = fetched.revision;
|
|
2148
|
+
await writeCache(source, logger, fetched.rows, fetched.revision);
|
|
2149
|
+
const result = { origin, rowCount: rowCount() };
|
|
2150
|
+
if (fetched.revision !== void 0) result.revision = fetched.revision;
|
|
2151
|
+
logPrimed(logger, result);
|
|
2152
|
+
return result;
|
|
2153
|
+
} catch (err) {
|
|
2154
|
+
logger.warn("[modelkit] catalog remote failed, trying cache", err);
|
|
2155
|
+
const error = describeError(err);
|
|
2156
|
+
try {
|
|
2157
|
+
const cached = await source.cache?.get();
|
|
2158
|
+
if (!cached) throw new Error("no cached catalog");
|
|
2159
|
+
const { rows, revision: rev } = parseEnvelope(JSON.parse(cached));
|
|
2160
|
+
liveCatalog.replace(rows);
|
|
2161
|
+
origin = "last-good";
|
|
2162
|
+
revision = rev;
|
|
2163
|
+
const result = { origin, rowCount: rowCount(), error };
|
|
2164
|
+
if (rev !== void 0) result.revision = rev;
|
|
2165
|
+
logPrimed(logger, result);
|
|
2166
|
+
return result;
|
|
2167
|
+
} catch (cacheErr) {
|
|
2168
|
+
logger.warn("[modelkit] catalog cache failed, using snapshot", cacheErr);
|
|
2169
|
+
liveCatalog.replace([...MODELS]);
|
|
2170
|
+
origin = "snapshot";
|
|
2171
|
+
revision = void 0;
|
|
2172
|
+
const result = {
|
|
2173
|
+
origin,
|
|
2174
|
+
rowCount: rowCount(),
|
|
2175
|
+
error
|
|
2176
|
+
};
|
|
2177
|
+
logPrimed(logger, result);
|
|
2178
|
+
return result;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
async function refresh(source, logger) {
|
|
2183
|
+
try {
|
|
2184
|
+
const fetched = await fetchRemote(source, logger);
|
|
2185
|
+
if ("kept" in fetched) return;
|
|
2186
|
+
liveCatalog.replace(fetched.rows);
|
|
2187
|
+
origin = "remote";
|
|
2188
|
+
revision = fetched.revision;
|
|
2189
|
+
await writeCache(source, logger, fetched.rows, fetched.revision);
|
|
2190
|
+
} catch (err) {
|
|
2191
|
+
logger.warn("[modelkit] catalog refresh failed, keeping current rows", err);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function primeCatalog(source) {
|
|
2195
|
+
if (memo) return memo;
|
|
2196
|
+
const logger = source.logger ?? console;
|
|
2197
|
+
memo = prime(source, logger).then((result) => {
|
|
2198
|
+
const ttlMs = source.ttlMs ?? 3e5;
|
|
2199
|
+
if (ttlMs > 0 && timer === void 0) {
|
|
2200
|
+
timer = setInterval(() => void refresh(source, logger), ttlMs);
|
|
2201
|
+
const maybeUnref = timer;
|
|
2202
|
+
maybeUnref.unref?.();
|
|
2203
|
+
}
|
|
2204
|
+
return result;
|
|
2205
|
+
});
|
|
2206
|
+
return memo;
|
|
2207
|
+
}
|
|
2208
|
+
function resetLiveCatalog() {
|
|
2209
|
+
if (timer !== void 0) {
|
|
2210
|
+
clearInterval(timer);
|
|
2211
|
+
timer = void 0;
|
|
2212
|
+
}
|
|
2213
|
+
memo = void 0;
|
|
2214
|
+
revision = void 0;
|
|
2215
|
+
origin = "snapshot";
|
|
2216
|
+
liveCatalog.replace([...MODELS]);
|
|
2217
|
+
}
|
|
2218
|
+
|
|
1824
2219
|
// src/resolve-options.ts
|
|
1825
2220
|
var SIZE_ORDER = ["512", "1K", "2K", "4K"];
|
|
1826
2221
|
var RESOLUTION_ORDER = ["360p", "480p", "720p", "768p", "1080p", "2k", "4k"];
|