@ariacompute/engine-ts 1.14.0 → 1.15.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/README.md +2 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +220 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# @ariacompute/engine-ts
|
|
2
2
|
|
|
3
3
|
```bash
|
|
4
|
-
export ARIA_FFI_LIB=/path/to/libaria_ffi.so
|
|
5
4
|
npm i @ariacompute/engine-ts
|
|
6
5
|
```
|
|
7
6
|
|
|
7
|
+
`libaria_ffi` is resolved as `ARIA_FFI_LIB` → package-bundled `dist/lib/` → `~/.ariacompute/lib/` (same as `aria-engine upgrade`). If none exist, `Engine.open` downloads the latest stable `libaria_ffi_{ver}_{os}.tar.gz` from regional Releases. `ARIA_FFI_LIB` remains an optional override.
|
|
8
|
+
|
|
8
9
|
```ts
|
|
9
10
|
import { Engine } from "@ariacompute/engine-ts";
|
|
10
11
|
const eng = new Engine("/path/to/bundle");
|
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,13 @@ export declare function hubFileUrls(source: "huggingface" | "modelscope", model:
|
|
|
53
53
|
* Matches aria-engine download: .com → Hugging Face, .cn → ModelScope.
|
|
54
54
|
* Dashboard is not used. Skips the download when a valid bundle is already cached. */
|
|
55
55
|
export declare function downloadModel(model: string, tokenOrOpts?: string | OpenOptions, site?: string): Promise<string>;
|
|
56
|
+
export declare function ffiLibName(platform?: string): string;
|
|
57
|
+
export declare function cachedFfiPath(platform?: string): string | undefined;
|
|
58
|
+
export declare function ffiAssetOs(platform?: string, arch?: string): string;
|
|
59
|
+
export declare function selectLatestStable(releases: Array<Record<string, unknown>>): string;
|
|
60
|
+
export declare function extractFfiArchive(archive: string, destDir: string, want?: string): string;
|
|
61
|
+
/** Return a path to libaria_ffi, downloading the latest stable Release if needed. */
|
|
62
|
+
export declare function ensureFfiLib(site?: string): Promise<string>;
|
|
56
63
|
export declare function isLocalRef(modelRef: string): boolean;
|
|
57
64
|
export declare class Engine {
|
|
58
65
|
private lib;
|
package/dist/index.js
CHANGED
|
@@ -41,12 +41,20 @@ exports.configYmlScalar = configYmlScalar;
|
|
|
41
41
|
exports.resolveHubToken = resolveHubToken;
|
|
42
42
|
exports.hubFileUrls = hubFileUrls;
|
|
43
43
|
exports.downloadModel = downloadModel;
|
|
44
|
+
exports.ffiLibName = ffiLibName;
|
|
45
|
+
exports.cachedFfiPath = cachedFfiPath;
|
|
46
|
+
exports.ffiAssetOs = ffiAssetOs;
|
|
47
|
+
exports.selectLatestStable = selectLatestStable;
|
|
48
|
+
exports.extractFfiArchive = extractFfiArchive;
|
|
49
|
+
exports.ensureFfiLib = ensureFfiLib;
|
|
44
50
|
exports.isLocalRef = isLocalRef;
|
|
51
|
+
const node_child_process_1 = require("node:child_process");
|
|
45
52
|
const fs = __importStar(require("node:fs"));
|
|
46
53
|
const os = __importStar(require("node:os"));
|
|
47
54
|
const path = __importStar(require("node:path"));
|
|
48
55
|
const promises_1 = require("node:stream/promises");
|
|
49
56
|
const node_stream_1 = require("node:stream");
|
|
57
|
+
const zlib = __importStar(require("node:zlib"));
|
|
50
58
|
const DEFAULT_SITE = "https://ariacompute.com";
|
|
51
59
|
const DEFAULT_SDK = "v1.0";
|
|
52
60
|
const HUB_REQUIRED = ["config.json", "weight.bin"];
|
|
@@ -282,10 +290,217 @@ async function downloadModel(model, tokenOrOpts, site = DEFAULT_SITE) {
|
|
|
282
290
|
throw e;
|
|
283
291
|
}
|
|
284
292
|
}
|
|
285
|
-
|
|
286
|
-
|
|
293
|
+
const SDK_UA = "aria-engine-sdk/0.1.0";
|
|
294
|
+
function ffiLibName(platform = process.platform) {
|
|
295
|
+
if (platform === "win32" || platform.toLowerCase().startsWith("win"))
|
|
296
|
+
return "aria_ffi.dll";
|
|
297
|
+
if (platform === "darwin")
|
|
298
|
+
return "libaria_ffi.dylib";
|
|
299
|
+
return "libaria_ffi.so";
|
|
300
|
+
}
|
|
301
|
+
function libDir() {
|
|
302
|
+
return path.join(ariaHome(), "lib");
|
|
303
|
+
}
|
|
304
|
+
function cachedFfiPath(platform = process.platform) {
|
|
305
|
+
const candidate = path.join(libDir(), ffiLibName(platform));
|
|
306
|
+
return fs.existsSync(candidate) ? candidate : undefined;
|
|
307
|
+
}
|
|
308
|
+
function bundledFfiPath() {
|
|
309
|
+
const candidate = path.join(__dirname, "lib", ffiLibName());
|
|
310
|
+
return fs.existsSync(candidate) ? candidate : undefined;
|
|
311
|
+
}
|
|
312
|
+
function ffiAssetOs(platform = process.platform, arch = process.arch) {
|
|
313
|
+
const p = platform.toLowerCase();
|
|
314
|
+
const a = arch.toLowerCase();
|
|
315
|
+
if (p === "linux" && (a === "x64" || a === "x86_64" || a === "amd64"))
|
|
316
|
+
return "linux_x86_64";
|
|
317
|
+
if (p === "linux" && (a === "arm64" || a === "aarch64"))
|
|
318
|
+
return "linux_arm64";
|
|
319
|
+
if (p === "darwin" || p === "macos")
|
|
320
|
+
return "macos";
|
|
321
|
+
if ((p === "win32" || p.startsWith("win")) && (a === "x64" || a === "x86_64" || a === "amd64")) {
|
|
322
|
+
return "windows_x86_64";
|
|
323
|
+
}
|
|
324
|
+
throw new Error(`unsupported platform ${platform}/${arch} for libaria_ffi`);
|
|
325
|
+
}
|
|
326
|
+
function stripV(tag) {
|
|
327
|
+
const t = tag.trim();
|
|
328
|
+
return t.startsWith("v") || t.startsWith("V") ? t.slice(1) : t;
|
|
329
|
+
}
|
|
330
|
+
function parseSemver(tag) {
|
|
331
|
+
const core = stripV(tag).split("-", 1)[0].split("+", 1)[0];
|
|
332
|
+
const parts = core.split(".");
|
|
333
|
+
if (!parts.length || !/^\d+$/.test(parts[0]))
|
|
334
|
+
return undefined;
|
|
335
|
+
return [
|
|
336
|
+
Number(parts[0]),
|
|
337
|
+
parts[1] && /^\d+$/.test(parts[1]) ? Number(parts[1]) : 0,
|
|
338
|
+
parts[2] && /^\d+$/.test(parts[2]) ? Number(parts[2]) : 0,
|
|
339
|
+
];
|
|
340
|
+
}
|
|
341
|
+
function selectLatestStable(releases) {
|
|
342
|
+
let bestTag;
|
|
343
|
+
let bestKey = [-1, -1, -1];
|
|
344
|
+
for (const rel of releases) {
|
|
345
|
+
if (rel.draft || rel.prerelease)
|
|
346
|
+
continue;
|
|
347
|
+
const tag = String(rel.tag_name || rel.tag || "");
|
|
348
|
+
const parsed = parseSemver(tag);
|
|
349
|
+
if (!parsed)
|
|
350
|
+
continue;
|
|
351
|
+
if (parsed[0] > bestKey[0] ||
|
|
352
|
+
(parsed[0] === bestKey[0] && parsed[1] > bestKey[1]) ||
|
|
353
|
+
(parsed[0] === bestKey[0] && parsed[1] === bestKey[1] && parsed[2] > bestKey[2])) {
|
|
354
|
+
bestKey = parsed;
|
|
355
|
+
bestTag = tag;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (!bestTag)
|
|
359
|
+
throw new Error("no stable release found for libaria_ffi");
|
|
360
|
+
return stripV(bestTag);
|
|
361
|
+
}
|
|
362
|
+
function upgradeOrg(site) {
|
|
363
|
+
const cfg = configYmlScalar("upgrade_url");
|
|
364
|
+
if (cfg)
|
|
365
|
+
return cfg.replace(/\/$/, "");
|
|
366
|
+
const hint = (site || configYmlScalar("site_url") || DEFAULT_SITE).toLowerCase();
|
|
367
|
+
if (hint.includes("ariacompute.cn") || hint.includes("gitee.com")) {
|
|
368
|
+
return "https://gitee.com/ariacompute";
|
|
369
|
+
}
|
|
370
|
+
return "https://github.com/ariacompute";
|
|
371
|
+
}
|
|
372
|
+
function releasesApiUrl(org) {
|
|
373
|
+
const owner = org.replace(/\/$/, "").split("/").pop() || "ariacompute";
|
|
374
|
+
if (org.toLowerCase().includes("gitee.com")) {
|
|
375
|
+
return `https://gitee.com/api/v5/repos/${owner}/engine/releases?per_page=30`;
|
|
376
|
+
}
|
|
377
|
+
return `https://api.github.com/repos/${owner}/engine/releases?per_page=30`;
|
|
378
|
+
}
|
|
379
|
+
function extractFfiArchive(archive, destDir, want = ffiLibName()) {
|
|
380
|
+
const tar = zlib.gunzipSync(fs.readFileSync(archive));
|
|
381
|
+
let offset = 0;
|
|
382
|
+
while (offset + 512 <= tar.length) {
|
|
383
|
+
const header = tar.subarray(offset, offset + 512);
|
|
384
|
+
if (header.every((b) => b === 0))
|
|
385
|
+
break;
|
|
386
|
+
const name = header.subarray(0, 100).toString("utf8").replace(/\0.*$/, "");
|
|
387
|
+
const sizeOctal = header.subarray(124, 136).toString("utf8").replace(/\0/g, "").trim();
|
|
388
|
+
const size = Number.parseInt(sizeOctal, 8) || 0;
|
|
389
|
+
const typeFlag = header[156];
|
|
390
|
+
offset += 512;
|
|
391
|
+
const isFile = typeFlag === 0 || typeFlag === 48; // '\0' or '0'
|
|
392
|
+
if (isFile && path.basename(name) === want) {
|
|
393
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
394
|
+
const dest = path.join(destDir, want);
|
|
395
|
+
fs.writeFileSync(dest, tar.subarray(offset, offset + size));
|
|
396
|
+
try {
|
|
397
|
+
fs.chmodSync(dest, 0o755);
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
/* windows */
|
|
401
|
+
}
|
|
402
|
+
return dest;
|
|
403
|
+
}
|
|
404
|
+
offset += Math.ceil(size / 512) * 512;
|
|
405
|
+
}
|
|
406
|
+
throw new Error(`${want} not found in ${archive}`);
|
|
407
|
+
}
|
|
408
|
+
function httpGetBytesSync(url) {
|
|
409
|
+
const script = 'fetch(process.env.ARIA_FFI_URL,{headers:{"User-Agent":"aria-engine-sdk/0.1.0"},redirect:"follow"}).then(async r=>{if(!r.ok){process.stderr.write("HTTP "+r.status);process.exit(1)}process.stdout.write(Buffer.from(await r.arrayBuffer()))}).catch(e=>{process.stderr.write(String(e&&e.message||e));process.exit(1)})';
|
|
410
|
+
const r = (0, node_child_process_1.spawnSync)(process.execPath, ["-e", script], {
|
|
411
|
+
env: { ...process.env, ARIA_FFI_URL: url },
|
|
412
|
+
encoding: "buffer",
|
|
413
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
414
|
+
});
|
|
415
|
+
if (r.status !== 0) {
|
|
416
|
+
throw new Error(`download failed: ${(r.stderr && r.stderr.toString()) || "exit " + r.status}`);
|
|
417
|
+
}
|
|
418
|
+
return r.stdout;
|
|
419
|
+
}
|
|
420
|
+
async function httpGetBytes(url) {
|
|
421
|
+
const resp = await fetch(url, { headers: { "User-Agent": SDK_UA }, redirect: "follow" });
|
|
422
|
+
if (!resp.ok)
|
|
423
|
+
throw new Error(`HTTP ${resp.status} ${url}`);
|
|
424
|
+
return Buffer.from(await resp.arrayBuffer());
|
|
425
|
+
}
|
|
426
|
+
function installFfiFromReleases(raw, archiveBytes) {
|
|
427
|
+
let releases;
|
|
428
|
+
try {
|
|
429
|
+
releases = JSON.parse(raw.toString("utf8"));
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
throw new Error("invalid releases JSON");
|
|
433
|
+
}
|
|
434
|
+
if (!Array.isArray(releases))
|
|
435
|
+
throw new Error("unexpected releases payload");
|
|
436
|
+
const ver = selectLatestStable(releases);
|
|
437
|
+
const assetName = `libaria_ffi_${ver}_${ffiAssetOs()}.tar.gz`;
|
|
438
|
+
let url;
|
|
439
|
+
for (const rel of releases) {
|
|
440
|
+
const tag = String(rel.tag_name || rel.tag || "");
|
|
441
|
+
if (stripV(tag) !== ver)
|
|
442
|
+
continue;
|
|
443
|
+
const assets = rel.assets || [];
|
|
444
|
+
for (const asset of assets) {
|
|
445
|
+
if (asset.name === assetName) {
|
|
446
|
+
url = asset.browser_download_url || asset.direct_asset_url;
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (url)
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
if (!url)
|
|
454
|
+
throw new Error(`release asset not found: ${assetName}`);
|
|
455
|
+
const staging = path.join(ariaHome(), "tmp", `ffi-${ver}`);
|
|
456
|
+
fs.rmSync(staging, { recursive: true, force: true });
|
|
457
|
+
fs.mkdirSync(staging, { recursive: true });
|
|
458
|
+
const archive = path.join(staging, assetName);
|
|
459
|
+
try {
|
|
460
|
+
fs.writeFileSync(archive, archiveBytes(url));
|
|
461
|
+
return extractFfiArchive(archive, libDir(), ffiLibName());
|
|
462
|
+
}
|
|
463
|
+
finally {
|
|
464
|
+
fs.rmSync(staging, { recursive: true, force: true });
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/** Return a path to libaria_ffi, downloading the latest stable Release if needed. */
|
|
468
|
+
async function ensureFfiLib(site) {
|
|
469
|
+
const env = process.env.ARIA_FFI_LIB;
|
|
470
|
+
if (env && fs.existsSync(env))
|
|
471
|
+
return env;
|
|
472
|
+
const bundled = bundledFfiPath();
|
|
473
|
+
if (bundled)
|
|
474
|
+
return bundled;
|
|
475
|
+
const cached = cachedFfiPath();
|
|
476
|
+
if (cached)
|
|
477
|
+
return cached;
|
|
478
|
+
const org = upgradeOrg(site);
|
|
479
|
+
const raw = await httpGetBytes(releasesApiUrl(org));
|
|
480
|
+
return installFfiFromReleases(raw, (assetUrl) => httpGetBytesSync(assetUrl));
|
|
481
|
+
}
|
|
482
|
+
function ensureFfiLibSync(site) {
|
|
483
|
+
const env = process.env.ARIA_FFI_LIB;
|
|
484
|
+
if (env && fs.existsSync(env))
|
|
485
|
+
return env;
|
|
486
|
+
const bundled = bundledFfiPath();
|
|
487
|
+
if (bundled)
|
|
488
|
+
return bundled;
|
|
489
|
+
const cached = cachedFfiPath();
|
|
490
|
+
if (cached)
|
|
491
|
+
return cached;
|
|
492
|
+
const org = upgradeOrg(site);
|
|
493
|
+
const raw = httpGetBytesSync(releasesApiUrl(org));
|
|
494
|
+
return installFfiFromReleases(raw, httpGetBytesSync);
|
|
495
|
+
}
|
|
496
|
+
function loadLib(ffiLib, site) {
|
|
497
|
+
const libPath = ffiLib ||
|
|
498
|
+
process.env.ARIA_FFI_LIB ||
|
|
499
|
+
bundledFfiPath() ||
|
|
500
|
+
cachedFfiPath() ||
|
|
501
|
+
ensureFfiLibSync(site);
|
|
287
502
|
if (!libPath)
|
|
288
|
-
throw new Error("
|
|
503
|
+
throw new Error("Cannot locate libaria_ffi");
|
|
289
504
|
const koffiNS = require("koffi");
|
|
290
505
|
return koffiNS.load(libPath);
|
|
291
506
|
}
|
|
@@ -295,7 +510,7 @@ function isLocalRef(modelRef) {
|
|
|
295
510
|
class Engine {
|
|
296
511
|
/** Construct from a local bundle directory. */
|
|
297
512
|
constructor(bundle, opts = {}) {
|
|
298
|
-
this.lib = loadLib(opts.ffiLib);
|
|
513
|
+
this.lib = loadLib(opts.ffiLib, opts.site);
|
|
299
514
|
this.fnInit = this.lib.func("aria_model_init", "void*", ["str"]);
|
|
300
515
|
this.fnDestroy = this.lib.func("aria_model_destroy", "void", ["void*"]);
|
|
301
516
|
this.fnComplete = this.lib.func("aria_complete", "int", ["void*", "str", "str", "str", "void*", "size_t"]);
|
|
@@ -311,6 +526,7 @@ class Engine {
|
|
|
311
526
|
/** Auto-detect: a value containing a separator or already on disk is a local
|
|
312
527
|
* path; otherwise it is a model name downloaded from the regional public hub. */
|
|
313
528
|
static async open(modelRef, opts = {}) {
|
|
529
|
+
await ensureFfiLib(opts.site);
|
|
314
530
|
if (isLocalRef(modelRef))
|
|
315
531
|
return new Engine(modelRef, opts);
|
|
316
532
|
const bundle = await downloadModel(modelRef, opts);
|