@camstack/addon-post-analysis 1.0.6 → 1.0.8
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/dist/{dist-fEh3l8TP.js → dist-BO4vHZS6.js} +1128 -1091
- package/dist/{dist-B8jIVWHd.mjs → dist-xZ1nMZGV.mjs} +1129 -1092
- package/dist/embedding-encoder/index.js +230 -3
- package/dist/embedding-encoder/index.mjs +232 -8
- package/dist/enrichment-engine/index.js +2 -2
- package/dist/enrichment-engine/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DJbVaeEQ.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CZHFM7Cg.mjs} +3 -3
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-k4z0mJjL.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BFIbMkkd.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-Bd2pLKxy.mjs → hostInit-BKhfonCX.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +4 -4
- package/dist/pipeline-analytics/index.mjs +3 -3
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/dist/{resolve-frame-q3KuC8k5.js → resolve-frame-A6QovqGd.js} +1 -1
- package/package.json +1 -1
|
@@ -2,13 +2,240 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-BO4vHZS6.js");
|
|
6
6
|
let node_path = require("node:path");
|
|
7
|
+
let node_path$1 = require_dist.__toESM(node_path, 1);
|
|
7
8
|
node_path = require_dist.__toESM(node_path);
|
|
8
9
|
let node_fs = require("node:fs");
|
|
10
|
+
let node_fs$1 = require_dist.__toESM(node_fs, 1);
|
|
9
11
|
node_fs = require_dist.__toESM(node_fs);
|
|
10
|
-
|
|
12
|
+
require("node:crypto");
|
|
11
13
|
let node_child_process = require("node:child_process");
|
|
14
|
+
//#region ../system/dist/model-download-service-C7AjBsX9.mjs
|
|
15
|
+
/** Build fetch headers, including HF auth token for huggingface.co URLs */
|
|
16
|
+
function buildHeaders(url) {
|
|
17
|
+
const headers = { "User-Agent": "CamStack/1.0" };
|
|
18
|
+
const hfToken = process.env["HF_TOKEN"] ?? process.env["HUGGING_FACE_HUB_TOKEN"];
|
|
19
|
+
if (hfToken && url.includes("huggingface.co")) headers["Authorization"] = `Bearer ${hfToken}`;
|
|
20
|
+
return headers;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Download a single file from a URL to a destination path.
|
|
24
|
+
* Uses native fetch() (Node 22+) which handles redirects natively.
|
|
25
|
+
* Streams to disk with optional progress callback.
|
|
26
|
+
* Returns the destination path. Skips download if file already exists.
|
|
27
|
+
*/
|
|
28
|
+
async function downloadFile(url, destPath, onProgress) {
|
|
29
|
+
if (node_fs$1.existsSync(destPath)) return destPath;
|
|
30
|
+
node_fs$1.mkdirSync(node_path$1.dirname(destPath), { recursive: true });
|
|
31
|
+
const tmpPath = destPath + ".downloading";
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
redirect: "follow",
|
|
35
|
+
headers: buildHeaders(url)
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} downloading ${url}`);
|
|
38
|
+
if (!response.body) throw new Error(`No response body from ${url}`);
|
|
39
|
+
const total = parseInt(response.headers.get("content-length") ?? "0", 10);
|
|
40
|
+
let downloaded = 0;
|
|
41
|
+
const fileStream = node_fs$1.createWriteStream(tmpPath);
|
|
42
|
+
const reader = response.body.getReader();
|
|
43
|
+
try {
|
|
44
|
+
for (;;) {
|
|
45
|
+
const { done, value } = await reader.read();
|
|
46
|
+
if (done || !value) break;
|
|
47
|
+
fileStream.write(value);
|
|
48
|
+
downloaded += value.length;
|
|
49
|
+
onProgress?.(downloaded, total);
|
|
50
|
+
}
|
|
51
|
+
} finally {
|
|
52
|
+
fileStream.end();
|
|
53
|
+
await new Promise((resolve, reject) => {
|
|
54
|
+
fileStream.on("finish", resolve);
|
|
55
|
+
fileStream.on("error", reject);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
node_fs$1.renameSync(tmpPath, destPath);
|
|
59
|
+
return destPath;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
try {
|
|
62
|
+
node_fs$1.unlinkSync(tmpPath);
|
|
63
|
+
} catch {}
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Fetch JSON from a URL using native fetch().
|
|
69
|
+
*/
|
|
70
|
+
async function fetchJson(url) {
|
|
71
|
+
const response = await fetch(url, {
|
|
72
|
+
redirect: "follow",
|
|
73
|
+
headers: buildHeaders(url)
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} fetching ${url}`);
|
|
76
|
+
return response.json();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Unified model download service.
|
|
80
|
+
*
|
|
81
|
+
* Handles downloading model files and extra files (labels, dicts) from a
|
|
82
|
+
* catalog of ModelCatalogEntry items. Supports single-file models and
|
|
83
|
+
* directory bundles (e.g., .mlpackage for CoreML).
|
|
84
|
+
*
|
|
85
|
+
* Addons use this via `context.models.ensure(modelId, format)`.
|
|
86
|
+
*/
|
|
87
|
+
var ModelDownloadService = class {
|
|
88
|
+
modelsDir;
|
|
89
|
+
onProgress;
|
|
90
|
+
catalog;
|
|
91
|
+
constructor(modelsDir, catalog, onProgress) {
|
|
92
|
+
this.modelsDir = modelsDir;
|
|
93
|
+
this.onProgress = onProgress;
|
|
94
|
+
const map = /* @__PURE__ */ new Map();
|
|
95
|
+
for (const entry of catalog) map.set(entry.id, entry);
|
|
96
|
+
this.catalog = map;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Ensure a model (and its extra files) is downloaded.
|
|
100
|
+
* Returns the local filesystem path to the model file/directory.
|
|
101
|
+
*/
|
|
102
|
+
async ensure(modelId, format) {
|
|
103
|
+
const entry = this.catalog.get(modelId);
|
|
104
|
+
if (!entry) throw new Error(`ModelDownloadService: unknown model "${modelId}"`);
|
|
105
|
+
const selectedFormat = format ?? this.pickDefaultFormat(entry);
|
|
106
|
+
const formatEntry = entry.formats[selectedFormat];
|
|
107
|
+
if (!formatEntry) throw new Error(`ModelDownloadService: model "${modelId}" has no ${selectedFormat} format`);
|
|
108
|
+
await this.ensureExtraFiles(modelId);
|
|
109
|
+
const modelPath = this.modelFilePath(entry, selectedFormat);
|
|
110
|
+
if (node_fs$1.existsSync(modelPath)) if (formatEntry.isDirectory) if (!node_fs$1.existsSync(node_path$1.join(modelPath, "Manifest.json"))) node_fs$1.rmSync(modelPath, {
|
|
111
|
+
recursive: true,
|
|
112
|
+
force: true
|
|
113
|
+
});
|
|
114
|
+
else return modelPath;
|
|
115
|
+
else return modelPath;
|
|
116
|
+
node_fs$1.mkdirSync(this.modelsDir, { recursive: true });
|
|
117
|
+
if (formatEntry.isDirectory) await this.downloadDirectory(formatEntry.url, modelPath, formatEntry.files, modelId);
|
|
118
|
+
else await downloadFile(formatEntry.url, modelPath, this.onProgress ? (downloaded, total) => this.onProgress(modelId, downloaded, total) : void 0);
|
|
119
|
+
return modelPath;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Ensure extra files for a model are downloaded.
|
|
123
|
+
* Returns the local paths of all extra files.
|
|
124
|
+
*/
|
|
125
|
+
async ensureExtraFiles(modelId) {
|
|
126
|
+
const entry = this.catalog.get(modelId);
|
|
127
|
+
if (!entry) throw new Error(`ModelDownloadService: unknown model "${modelId}"`);
|
|
128
|
+
const extras = entry.extraFiles;
|
|
129
|
+
if (!extras || extras.length === 0) return [];
|
|
130
|
+
const paths = [];
|
|
131
|
+
for (const extra of extras) {
|
|
132
|
+
const destPath = node_path$1.join(this.modelsDir, extra.filename);
|
|
133
|
+
await downloadFile(extra.url, destPath);
|
|
134
|
+
paths.push(destPath);
|
|
135
|
+
}
|
|
136
|
+
return paths;
|
|
137
|
+
}
|
|
138
|
+
/** Absolute path to the shared models directory. */
|
|
139
|
+
getModelsDir() {
|
|
140
|
+
return this.modelsDir;
|
|
141
|
+
}
|
|
142
|
+
/** Check if a model file is already present on disk. */
|
|
143
|
+
isDownloaded(modelId, format) {
|
|
144
|
+
const entry = this.catalog.get(modelId);
|
|
145
|
+
if (!entry) return false;
|
|
146
|
+
const selectedFormat = format ?? this.pickDefaultFormat(entry);
|
|
147
|
+
const formatEntry = entry.formats[selectedFormat];
|
|
148
|
+
if (!formatEntry) return false;
|
|
149
|
+
const modelPath = this.modelFilePath(entry, selectedFormat);
|
|
150
|
+
if (!node_fs$1.existsSync(modelPath)) return false;
|
|
151
|
+
if (formatEntry.isDirectory) return node_fs$1.existsSync(node_path$1.join(modelPath, "Manifest.json"));
|
|
152
|
+
return node_fs$1.statSync(modelPath).size > 0;
|
|
153
|
+
}
|
|
154
|
+
/** Get the catalog entry for a model by ID. */
|
|
155
|
+
getEntry(modelId) {
|
|
156
|
+
return this.catalog.get(modelId);
|
|
157
|
+
}
|
|
158
|
+
pickDefaultFormat(entry) {
|
|
159
|
+
for (const fmt of [
|
|
160
|
+
"onnx",
|
|
161
|
+
"coreml",
|
|
162
|
+
"openvino",
|
|
163
|
+
"tflite",
|
|
164
|
+
"pt"
|
|
165
|
+
]) if (entry.formats[fmt]) return fmt;
|
|
166
|
+
const first = Object.keys(entry.formats)[0];
|
|
167
|
+
if (first) return first;
|
|
168
|
+
throw new Error(`ModelDownloadService: model "${entry.id}" has no formats`);
|
|
169
|
+
}
|
|
170
|
+
modelFilePath(entry, format) {
|
|
171
|
+
const formatEntry = entry.formats[format];
|
|
172
|
+
if (!formatEntry) throw new Error(`Model ${entry.id} has no ${format} format`);
|
|
173
|
+
const urlParts = formatEntry.url.split("/");
|
|
174
|
+
const filename = urlParts[urlParts.length - 1] ?? `${entry.id}.${format}`;
|
|
175
|
+
return node_path$1.join(this.modelsDir, filename);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Download a directory bundle (e.g., .mlpackage) from HuggingFace.
|
|
179
|
+
* ATOMIC: downloads to temp dir, renames only on complete success.
|
|
180
|
+
*/
|
|
181
|
+
async downloadDirectory(url, destDir, knownFiles, _modelId) {
|
|
182
|
+
const match = url.match(/huggingface\.co\/([^/]+\/[^/]+)\/resolve\/main\/(.+)/);
|
|
183
|
+
if (!match) throw new Error(`Cannot parse HuggingFace URL: ${url}`);
|
|
184
|
+
const [, repo, dirPath] = match;
|
|
185
|
+
let filesToDownload;
|
|
186
|
+
if (knownFiles && knownFiles.length > 0) filesToDownload = knownFiles.map((f) => ({
|
|
187
|
+
relativePath: f,
|
|
188
|
+
fileUrl: `https://huggingface.co/${repo}/resolve/main/${dirPath}/${f}`
|
|
189
|
+
}));
|
|
190
|
+
else {
|
|
191
|
+
const hfFiles = await this.listHfFiles(repo, dirPath);
|
|
192
|
+
if (hfFiles.length === 0) throw new Error(`No files found in HuggingFace directory: ${dirPath}`);
|
|
193
|
+
filesToDownload = hfFiles.map((f) => ({
|
|
194
|
+
relativePath: f.path.substring(dirPath.length + 1),
|
|
195
|
+
fileUrl: `https://huggingface.co/${repo}/resolve/main/${f.path}`
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
const tmpDir = destDir + ".downloading";
|
|
199
|
+
node_fs$1.rmSync(tmpDir, {
|
|
200
|
+
recursive: true,
|
|
201
|
+
force: true
|
|
202
|
+
});
|
|
203
|
+
node_fs$1.mkdirSync(tmpDir, { recursive: true });
|
|
204
|
+
try {
|
|
205
|
+
for (const file of filesToDownload) {
|
|
206
|
+
const destPath = node_path$1.join(tmpDir, file.relativePath);
|
|
207
|
+
node_fs$1.mkdirSync(node_path$1.dirname(destPath), { recursive: true });
|
|
208
|
+
await downloadFile(file.fileUrl, destPath);
|
|
209
|
+
}
|
|
210
|
+
node_fs$1.rmSync(destDir, {
|
|
211
|
+
recursive: true,
|
|
212
|
+
force: true
|
|
213
|
+
});
|
|
214
|
+
node_fs$1.renameSync(tmpDir, destDir);
|
|
215
|
+
} catch (err) {
|
|
216
|
+
node_fs$1.rmSync(tmpDir, {
|
|
217
|
+
recursive: true,
|
|
218
|
+
force: true
|
|
219
|
+
});
|
|
220
|
+
throw err;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** Recursively list all files in a HuggingFace directory via API. */
|
|
224
|
+
async listHfFiles(repo, dirPath) {
|
|
225
|
+
const entries = await fetchJson(`https://huggingface.co/api/models/${repo}/tree/main/${dirPath}`);
|
|
226
|
+
const files = [];
|
|
227
|
+
for (const entry of entries) if (entry.type === "file") files.push({
|
|
228
|
+
path: entry.path,
|
|
229
|
+
size: entry.size ?? entry.lfs?.size ?? 0
|
|
230
|
+
});
|
|
231
|
+
else if (entry.type === "directory") {
|
|
232
|
+
const subFiles = await this.listHfFiles(repo, entry.path);
|
|
233
|
+
files.push(...subFiles);
|
|
234
|
+
}
|
|
235
|
+
return files;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
//#endregion
|
|
12
239
|
//#region src/embedding-encoder/catalogs/embedding-models.ts
|
|
13
240
|
var CLIP_IMAGE_MODELS = [
|
|
14
241
|
{
|
|
@@ -321,7 +548,7 @@ var EmbeddingEncoderAddon = class extends require_dist.BaseAddon {
|
|
|
321
548
|
location: "models",
|
|
322
549
|
relativePath: ""
|
|
323
550
|
}).catch(() => "camstack-data/models");
|
|
324
|
-
this.models = new
|
|
551
|
+
this.models = new ModelDownloadService(modelsDir, []);
|
|
325
552
|
return [{
|
|
326
553
|
capability: require_dist.embeddingEncoderCapability,
|
|
327
554
|
provider: this
|
|
@@ -1,12 +1,236 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as BaseAddon, i as embeddingEncoderCapability } from "../dist-xZ1nMZGV.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import * as path from "node:path";
|
|
3
|
+
import * as path$1 from "node:path";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
|
-
import { ModelDownloadService } from "@camstack/system";
|
|
6
5
|
import { spawn } from "node:child_process";
|
|
7
6
|
//#region \0rolldown/runtime.js
|
|
8
7
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
9
8
|
//#endregion
|
|
9
|
+
//#region ../system/dist/model-download-service-C7AjBsX9.mjs
|
|
10
|
+
/** Build fetch headers, including HF auth token for huggingface.co URLs */
|
|
11
|
+
function buildHeaders(url) {
|
|
12
|
+
const headers = { "User-Agent": "CamStack/1.0" };
|
|
13
|
+
const hfToken = process.env["HF_TOKEN"] ?? process.env["HUGGING_FACE_HUB_TOKEN"];
|
|
14
|
+
if (hfToken && url.includes("huggingface.co")) headers["Authorization"] = `Bearer ${hfToken}`;
|
|
15
|
+
return headers;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Download a single file from a URL to a destination path.
|
|
19
|
+
* Uses native fetch() (Node 22+) which handles redirects natively.
|
|
20
|
+
* Streams to disk with optional progress callback.
|
|
21
|
+
* Returns the destination path. Skips download if file already exists.
|
|
22
|
+
*/
|
|
23
|
+
async function downloadFile(url, destPath, onProgress) {
|
|
24
|
+
if (fs.existsSync(destPath)) return destPath;
|
|
25
|
+
fs.mkdirSync(path$1.dirname(destPath), { recursive: true });
|
|
26
|
+
const tmpPath = destPath + ".downloading";
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(url, {
|
|
29
|
+
redirect: "follow",
|
|
30
|
+
headers: buildHeaders(url)
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} downloading ${url}`);
|
|
33
|
+
if (!response.body) throw new Error(`No response body from ${url}`);
|
|
34
|
+
const total = parseInt(response.headers.get("content-length") ?? "0", 10);
|
|
35
|
+
let downloaded = 0;
|
|
36
|
+
const fileStream = fs.createWriteStream(tmpPath);
|
|
37
|
+
const reader = response.body.getReader();
|
|
38
|
+
try {
|
|
39
|
+
for (;;) {
|
|
40
|
+
const { done, value } = await reader.read();
|
|
41
|
+
if (done || !value) break;
|
|
42
|
+
fileStream.write(value);
|
|
43
|
+
downloaded += value.length;
|
|
44
|
+
onProgress?.(downloaded, total);
|
|
45
|
+
}
|
|
46
|
+
} finally {
|
|
47
|
+
fileStream.end();
|
|
48
|
+
await new Promise((resolve, reject) => {
|
|
49
|
+
fileStream.on("finish", resolve);
|
|
50
|
+
fileStream.on("error", reject);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
fs.renameSync(tmpPath, destPath);
|
|
54
|
+
return destPath;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
try {
|
|
57
|
+
fs.unlinkSync(tmpPath);
|
|
58
|
+
} catch {}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Fetch JSON from a URL using native fetch().
|
|
64
|
+
*/
|
|
65
|
+
async function fetchJson(url) {
|
|
66
|
+
const response = await fetch(url, {
|
|
67
|
+
redirect: "follow",
|
|
68
|
+
headers: buildHeaders(url)
|
|
69
|
+
});
|
|
70
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} fetching ${url}`);
|
|
71
|
+
return response.json();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Unified model download service.
|
|
75
|
+
*
|
|
76
|
+
* Handles downloading model files and extra files (labels, dicts) from a
|
|
77
|
+
* catalog of ModelCatalogEntry items. Supports single-file models and
|
|
78
|
+
* directory bundles (e.g., .mlpackage for CoreML).
|
|
79
|
+
*
|
|
80
|
+
* Addons use this via `context.models.ensure(modelId, format)`.
|
|
81
|
+
*/
|
|
82
|
+
var ModelDownloadService = class {
|
|
83
|
+
modelsDir;
|
|
84
|
+
onProgress;
|
|
85
|
+
catalog;
|
|
86
|
+
constructor(modelsDir, catalog, onProgress) {
|
|
87
|
+
this.modelsDir = modelsDir;
|
|
88
|
+
this.onProgress = onProgress;
|
|
89
|
+
const map = /* @__PURE__ */ new Map();
|
|
90
|
+
for (const entry of catalog) map.set(entry.id, entry);
|
|
91
|
+
this.catalog = map;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Ensure a model (and its extra files) is downloaded.
|
|
95
|
+
* Returns the local filesystem path to the model file/directory.
|
|
96
|
+
*/
|
|
97
|
+
async ensure(modelId, format) {
|
|
98
|
+
const entry = this.catalog.get(modelId);
|
|
99
|
+
if (!entry) throw new Error(`ModelDownloadService: unknown model "${modelId}"`);
|
|
100
|
+
const selectedFormat = format ?? this.pickDefaultFormat(entry);
|
|
101
|
+
const formatEntry = entry.formats[selectedFormat];
|
|
102
|
+
if (!formatEntry) throw new Error(`ModelDownloadService: model "${modelId}" has no ${selectedFormat} format`);
|
|
103
|
+
await this.ensureExtraFiles(modelId);
|
|
104
|
+
const modelPath = this.modelFilePath(entry, selectedFormat);
|
|
105
|
+
if (fs.existsSync(modelPath)) if (formatEntry.isDirectory) if (!fs.existsSync(path$1.join(modelPath, "Manifest.json"))) fs.rmSync(modelPath, {
|
|
106
|
+
recursive: true,
|
|
107
|
+
force: true
|
|
108
|
+
});
|
|
109
|
+
else return modelPath;
|
|
110
|
+
else return modelPath;
|
|
111
|
+
fs.mkdirSync(this.modelsDir, { recursive: true });
|
|
112
|
+
if (formatEntry.isDirectory) await this.downloadDirectory(formatEntry.url, modelPath, formatEntry.files, modelId);
|
|
113
|
+
else await downloadFile(formatEntry.url, modelPath, this.onProgress ? (downloaded, total) => this.onProgress(modelId, downloaded, total) : void 0);
|
|
114
|
+
return modelPath;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Ensure extra files for a model are downloaded.
|
|
118
|
+
* Returns the local paths of all extra files.
|
|
119
|
+
*/
|
|
120
|
+
async ensureExtraFiles(modelId) {
|
|
121
|
+
const entry = this.catalog.get(modelId);
|
|
122
|
+
if (!entry) throw new Error(`ModelDownloadService: unknown model "${modelId}"`);
|
|
123
|
+
const extras = entry.extraFiles;
|
|
124
|
+
if (!extras || extras.length === 0) return [];
|
|
125
|
+
const paths = [];
|
|
126
|
+
for (const extra of extras) {
|
|
127
|
+
const destPath = path$1.join(this.modelsDir, extra.filename);
|
|
128
|
+
await downloadFile(extra.url, destPath);
|
|
129
|
+
paths.push(destPath);
|
|
130
|
+
}
|
|
131
|
+
return paths;
|
|
132
|
+
}
|
|
133
|
+
/** Absolute path to the shared models directory. */
|
|
134
|
+
getModelsDir() {
|
|
135
|
+
return this.modelsDir;
|
|
136
|
+
}
|
|
137
|
+
/** Check if a model file is already present on disk. */
|
|
138
|
+
isDownloaded(modelId, format) {
|
|
139
|
+
const entry = this.catalog.get(modelId);
|
|
140
|
+
if (!entry) return false;
|
|
141
|
+
const selectedFormat = format ?? this.pickDefaultFormat(entry);
|
|
142
|
+
const formatEntry = entry.formats[selectedFormat];
|
|
143
|
+
if (!formatEntry) return false;
|
|
144
|
+
const modelPath = this.modelFilePath(entry, selectedFormat);
|
|
145
|
+
if (!fs.existsSync(modelPath)) return false;
|
|
146
|
+
if (formatEntry.isDirectory) return fs.existsSync(path$1.join(modelPath, "Manifest.json"));
|
|
147
|
+
return fs.statSync(modelPath).size > 0;
|
|
148
|
+
}
|
|
149
|
+
/** Get the catalog entry for a model by ID. */
|
|
150
|
+
getEntry(modelId) {
|
|
151
|
+
return this.catalog.get(modelId);
|
|
152
|
+
}
|
|
153
|
+
pickDefaultFormat(entry) {
|
|
154
|
+
for (const fmt of [
|
|
155
|
+
"onnx",
|
|
156
|
+
"coreml",
|
|
157
|
+
"openvino",
|
|
158
|
+
"tflite",
|
|
159
|
+
"pt"
|
|
160
|
+
]) if (entry.formats[fmt]) return fmt;
|
|
161
|
+
const first = Object.keys(entry.formats)[0];
|
|
162
|
+
if (first) return first;
|
|
163
|
+
throw new Error(`ModelDownloadService: model "${entry.id}" has no formats`);
|
|
164
|
+
}
|
|
165
|
+
modelFilePath(entry, format) {
|
|
166
|
+
const formatEntry = entry.formats[format];
|
|
167
|
+
if (!formatEntry) throw new Error(`Model ${entry.id} has no ${format} format`);
|
|
168
|
+
const urlParts = formatEntry.url.split("/");
|
|
169
|
+
const filename = urlParts[urlParts.length - 1] ?? `${entry.id}.${format}`;
|
|
170
|
+
return path$1.join(this.modelsDir, filename);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Download a directory bundle (e.g., .mlpackage) from HuggingFace.
|
|
174
|
+
* ATOMIC: downloads to temp dir, renames only on complete success.
|
|
175
|
+
*/
|
|
176
|
+
async downloadDirectory(url, destDir, knownFiles, _modelId) {
|
|
177
|
+
const match = url.match(/huggingface\.co\/([^/]+\/[^/]+)\/resolve\/main\/(.+)/);
|
|
178
|
+
if (!match) throw new Error(`Cannot parse HuggingFace URL: ${url}`);
|
|
179
|
+
const [, repo, dirPath] = match;
|
|
180
|
+
let filesToDownload;
|
|
181
|
+
if (knownFiles && knownFiles.length > 0) filesToDownload = knownFiles.map((f) => ({
|
|
182
|
+
relativePath: f,
|
|
183
|
+
fileUrl: `https://huggingface.co/${repo}/resolve/main/${dirPath}/${f}`
|
|
184
|
+
}));
|
|
185
|
+
else {
|
|
186
|
+
const hfFiles = await this.listHfFiles(repo, dirPath);
|
|
187
|
+
if (hfFiles.length === 0) throw new Error(`No files found in HuggingFace directory: ${dirPath}`);
|
|
188
|
+
filesToDownload = hfFiles.map((f) => ({
|
|
189
|
+
relativePath: f.path.substring(dirPath.length + 1),
|
|
190
|
+
fileUrl: `https://huggingface.co/${repo}/resolve/main/${f.path}`
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
const tmpDir = destDir + ".downloading";
|
|
194
|
+
fs.rmSync(tmpDir, {
|
|
195
|
+
recursive: true,
|
|
196
|
+
force: true
|
|
197
|
+
});
|
|
198
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
199
|
+
try {
|
|
200
|
+
for (const file of filesToDownload) {
|
|
201
|
+
const destPath = path$1.join(tmpDir, file.relativePath);
|
|
202
|
+
fs.mkdirSync(path$1.dirname(destPath), { recursive: true });
|
|
203
|
+
await downloadFile(file.fileUrl, destPath);
|
|
204
|
+
}
|
|
205
|
+
fs.rmSync(destDir, {
|
|
206
|
+
recursive: true,
|
|
207
|
+
force: true
|
|
208
|
+
});
|
|
209
|
+
fs.renameSync(tmpDir, destDir);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
fs.rmSync(tmpDir, {
|
|
212
|
+
recursive: true,
|
|
213
|
+
force: true
|
|
214
|
+
});
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/** Recursively list all files in a HuggingFace directory via API. */
|
|
219
|
+
async listHfFiles(repo, dirPath) {
|
|
220
|
+
const entries = await fetchJson(`https://huggingface.co/api/models/${repo}/tree/main/${dirPath}`);
|
|
221
|
+
const files = [];
|
|
222
|
+
for (const entry of entries) if (entry.type === "file") files.push({
|
|
223
|
+
path: entry.path,
|
|
224
|
+
size: entry.size ?? entry.lfs?.size ?? 0
|
|
225
|
+
});
|
|
226
|
+
else if (entry.type === "directory") {
|
|
227
|
+
const subFiles = await this.listHfFiles(repo, entry.path);
|
|
228
|
+
files.push(...subFiles);
|
|
229
|
+
}
|
|
230
|
+
return files;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
//#endregion
|
|
10
234
|
//#region src/embedding-encoder/catalogs/embedding-models.ts
|
|
11
235
|
var CLIP_IMAGE_MODELS = [
|
|
12
236
|
{
|
|
@@ -391,8 +615,8 @@ var EmbeddingEncoderAddon = class extends BaseAddon {
|
|
|
391
615
|
const pythonPath = await this.ctx.deps.ensurePython();
|
|
392
616
|
if (!pythonPath) throw new Error("EmbeddingEncoder: embedded Python is unavailable — cannot run ONNX embeddings. ctx.deps.ensurePython() returned null (portable Python download likely failed).");
|
|
393
617
|
const pythonDir = resolveEmbeddingPythonDir();
|
|
394
|
-
await this.ctx.deps.installPythonRequirements(path.join(pythonDir, "requirements-embedding.txt"));
|
|
395
|
-
const rawEngine = new PythonRawTensorEngine(pythonPath, path.join(pythonDir, "raw_tensor_inference.py"), modelPath, engineLogger);
|
|
618
|
+
await this.ctx.deps.installPythonRequirements(path$1.join(pythonDir, "requirements-embedding.txt"));
|
|
619
|
+
const rawEngine = new PythonRawTensorEngine(pythonPath, path$1.join(pythonDir, "raw_tensor_inference.py"), modelPath, engineLogger);
|
|
396
620
|
await rawEngine.initialize();
|
|
397
621
|
if (target === "image") this.imageRawEngine = rawEngine;
|
|
398
622
|
else this.textRawEngine = rawEngine;
|
|
@@ -478,10 +702,10 @@ function resolveEmbeddingPythonDir() {
|
|
|
478
702
|
const candidates = [];
|
|
479
703
|
try {
|
|
480
704
|
const pkgPath = __require.resolve("@camstack/addon-post-analysis/package.json");
|
|
481
|
-
candidates.push(path.join(path.dirname(pkgPath), "python"));
|
|
705
|
+
candidates.push(path$1.join(path$1.dirname(pkgPath), "python"));
|
|
482
706
|
} catch {}
|
|
483
|
-
candidates.push(path.join(__dirname, "../../python"), path.join(__dirname, "../../../python"), path.join(__dirname, "../python"), path.join(__dirname, "../../../../python"));
|
|
484
|
-
for (const c of candidates) if (fs.existsSync(path.join(c, "raw_tensor_inference.py"))) return c;
|
|
707
|
+
candidates.push(path$1.join(__dirname, "../../python"), path$1.join(__dirname, "../../../python"), path$1.join(__dirname, "../python"), path$1.join(__dirname, "../../../../python"));
|
|
708
|
+
for (const c of candidates) if (fs.existsSync(path$1.join(c, "raw_tensor_inference.py"))) return c;
|
|
485
709
|
throw new Error(`EmbeddingEncoder: python/ dir (raw_tensor_inference.py) not found. Searched:\n${candidates.join("\n")}`);
|
|
486
710
|
}
|
|
487
711
|
//#endregion
|
|
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_resolve_frame = require("../resolve-frame-
|
|
5
|
+
const require_dist = require("../dist-BO4vHZS6.js");
|
|
6
|
+
const require_resolve_frame = require("../resolve-frame-A6QovqGd.js");
|
|
7
7
|
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
8
8
|
//#region src/enrichment-engine/types.ts
|
|
9
9
|
var DEFAULT_ENRICHMENT_CONFIG = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as tuple, S as string, _ as _enum,
|
|
1
|
+
import { C as tuple, S as string, _ as _enum, b as number, d as BaseAddon, h as createEvent, m as asJsonObject, p as EventCategory, v as array, x as object, y as boolean } from "../dist-xZ1nMZGV.mjs";
|
|
2
2
|
import { n as extractCrop, t as resolveFrame } from "../resolve-frame-CT1T1tWy.mjs";
|
|
3
3
|
import { FrameRingReaderCache } from "@camstack/shm-ring";
|
|
4
4
|
//#region src/enrichment-engine/types.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as e, i as t, n, o as r, r as i, t as a } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare__react__loadShare__.js-C0AuF9av.mjs";
|
|
2
2
|
import { t as o } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-B3Wx5J80.mjs";
|
|
3
|
-
import { a as s, i as c, n as l, o as u, r as d, s as f, t as p } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-
|
|
3
|
+
import { a as s, i as c, n as l, o as u, r as d, s as f, t as p } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BFIbMkkd.mjs";
|
|
4
4
|
import { n as m, r as h, t as g } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-Bm-iyjmq.mjs";
|
|
5
5
|
//#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
|
|
6
6
|
var _ = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), v = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), y = (e) => {
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.0.
|
|
6
|
+
version: "1.0.7",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.0.
|
|
21
|
+
version: "1.0.7",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.0.
|
|
36
|
+
version: "1.0.7",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_pipeline_analytics_widgets",
|