@ariacompute/engine-rn 1.14.1 → 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 -0
- package/package.json +1 -1
- package/src/index.js +158 -0
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ console.log(await eng.complete([{ role: 'user', content: 'hi' }]));
|
|
|
8
8
|
|
|
9
9
|
npm publish name: `@ariacompute/engine-rn`. Device-farm CI: `.github/workflows/bindings-mobile.yml`.
|
|
10
10
|
|
|
11
|
+
`AriaEngine.open` installs `libaria_ffi` into `~/.ariacompute/lib/` when it is not already on `ARIA_FFI_LIB` or in that cache (same Releases asset as `aria-engine upgrade`).
|
|
12
|
+
|
|
11
13
|
## Auto-download by model name
|
|
12
14
|
|
|
13
15
|
`AriaEngine.open(modelRef, { token, site })` accepts a local bundle path **or**
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -261,6 +261,163 @@ async function downloadModel(model, tokenOrOpts, site = DEFAULT_SITE) {
|
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
const SDK_UA = 'aria-engine-sdk/0.1.0';
|
|
265
|
+
const zlib = require('zlib');
|
|
266
|
+
|
|
267
|
+
function ffiLibName(platform = process.platform) {
|
|
268
|
+
if (platform === 'win32' || String(platform).toLowerCase().startsWith('win')) return 'aria_ffi.dll';
|
|
269
|
+
if (platform === 'darwin') return 'libaria_ffi.dylib';
|
|
270
|
+
return 'libaria_ffi.so';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function libDir() {
|
|
274
|
+
const path = require('path');
|
|
275
|
+
return path.join(ariaHome(), 'lib');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function cachedFfiPath(platform = process.platform) {
|
|
279
|
+
const path = require('path');
|
|
280
|
+
const fs = require('fs');
|
|
281
|
+
const candidate = path.join(libDir(), ffiLibName(platform));
|
|
282
|
+
return fs.existsSync(candidate) ? candidate : undefined;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function ffiAssetOs(platform = process.platform, arch = process.arch) {
|
|
286
|
+
const p = String(platform).toLowerCase();
|
|
287
|
+
const a = String(arch).toLowerCase();
|
|
288
|
+
if (p === 'linux' && (a === 'x64' || a === 'x86_64' || a === 'amd64')) return 'linux_x86_64';
|
|
289
|
+
if (p === 'linux' && (a === 'arm64' || a === 'aarch64')) return 'linux_arm64';
|
|
290
|
+
if (p === 'darwin' || p === 'macos') return 'macos';
|
|
291
|
+
if ((p === 'win32' || p.startsWith('win')) && (a === 'x64' || a === 'x86_64' || a === 'amd64')) {
|
|
292
|
+
return 'windows_x86_64';
|
|
293
|
+
}
|
|
294
|
+
throw new Error(`unsupported platform ${platform}/${arch} for libaria_ffi`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function stripV(tag) {
|
|
298
|
+
const t = String(tag).trim();
|
|
299
|
+
return t.startsWith('v') || t.startsWith('V') ? t.slice(1) : t;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function selectLatestStable(releases) {
|
|
303
|
+
let bestTag;
|
|
304
|
+
let bestKey = [-1, -1, -1];
|
|
305
|
+
for (const rel of releases) {
|
|
306
|
+
if (rel.draft || rel.prerelease) continue;
|
|
307
|
+
const tag = String(rel.tag_name || rel.tag || '');
|
|
308
|
+
const core = stripV(tag).split('-')[0].split('+')[0];
|
|
309
|
+
const parts = core.split('.');
|
|
310
|
+
if (!parts.length || !/^\d+$/.test(parts[0])) continue;
|
|
311
|
+
const parsed = [
|
|
312
|
+
Number(parts[0]),
|
|
313
|
+
parts[1] && /^\d+$/.test(parts[1]) ? Number(parts[1]) : 0,
|
|
314
|
+
parts[2] && /^\d+$/.test(parts[2]) ? Number(parts[2]) : 0,
|
|
315
|
+
];
|
|
316
|
+
if (
|
|
317
|
+
parsed[0] > bestKey[0] ||
|
|
318
|
+
(parsed[0] === bestKey[0] && parsed[1] > bestKey[1]) ||
|
|
319
|
+
(parsed[0] === bestKey[0] && parsed[1] === bestKey[1] && parsed[2] > bestKey[2])
|
|
320
|
+
) {
|
|
321
|
+
bestKey = parsed;
|
|
322
|
+
bestTag = tag;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (!bestTag) throw new Error('no stable release found for libaria_ffi');
|
|
326
|
+
return stripV(bestTag);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function upgradeOrg(site) {
|
|
330
|
+
const cfg = configYmlScalar('upgrade_url');
|
|
331
|
+
if (cfg) return cfg.replace(/\/$/, '');
|
|
332
|
+
const hint = String(site || configYmlScalar('site_url') || DEFAULT_SITE).toLowerCase();
|
|
333
|
+
if (hint.includes('ariacompute.cn') || hint.includes('gitee.com')) {
|
|
334
|
+
return 'https://gitee.com/ariacompute';
|
|
335
|
+
}
|
|
336
|
+
return 'https://github.com/ariacompute';
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function releasesApiUrl(org) {
|
|
340
|
+
const owner = org.replace(/\/$/, '').split('/').pop() || 'ariacompute';
|
|
341
|
+
if (org.toLowerCase().includes('gitee.com')) {
|
|
342
|
+
return `https://gitee.com/api/v5/repos/${owner}/engine/releases?per_page=30`;
|
|
343
|
+
}
|
|
344
|
+
return `https://api.github.com/repos/${owner}/engine/releases?per_page=30`;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function extractFfiArchive(archive, destDir, want = ffiLibName()) {
|
|
348
|
+
const fs = require('fs');
|
|
349
|
+
const path = require('path');
|
|
350
|
+
const tar = zlib.gunzipSync(fs.readFileSync(archive));
|
|
351
|
+
let offset = 0;
|
|
352
|
+
while (offset + 512 <= tar.length) {
|
|
353
|
+
const header = tar.subarray(offset, offset + 512);
|
|
354
|
+
if (header.every((b) => b === 0)) break;
|
|
355
|
+
const name = header.subarray(0, 100).toString('utf8').replace(/\0.*$/, '');
|
|
356
|
+
const sizeOctal = header.subarray(124, 136).toString('utf8').replace(/\0/g, '').trim();
|
|
357
|
+
const size = Number.parseInt(sizeOctal, 8) || 0;
|
|
358
|
+
const typeFlag = header[156];
|
|
359
|
+
offset += 512;
|
|
360
|
+
const isFile = typeFlag === 0 || typeFlag === 48;
|
|
361
|
+
if (isFile && path.basename(name) === want) {
|
|
362
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
363
|
+
const dest = path.join(destDir, want);
|
|
364
|
+
fs.writeFileSync(dest, tar.subarray(offset, offset + size));
|
|
365
|
+
try {
|
|
366
|
+
fs.chmodSync(dest, 0o755);
|
|
367
|
+
} catch {
|
|
368
|
+
/* windows */
|
|
369
|
+
}
|
|
370
|
+
return dest;
|
|
371
|
+
}
|
|
372
|
+
offset += Math.ceil(size / 512) * 512;
|
|
373
|
+
}
|
|
374
|
+
throw new Error(`${want} not found in ${archive}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function httpGetBytes(url) {
|
|
378
|
+
const resp = await fetch(url, { headers: { 'User-Agent': SDK_UA }, redirect: 'follow' });
|
|
379
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status} ${url}`);
|
|
380
|
+
return Buffer.from(await resp.arrayBuffer());
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function ensureFfiLib(site) {
|
|
384
|
+
const fs = require('fs');
|
|
385
|
+
const path = require('path');
|
|
386
|
+
const env = process.env.ARIA_FFI_LIB;
|
|
387
|
+
if (env && fs.existsSync(env)) return env;
|
|
388
|
+
const cached = cachedFfiPath();
|
|
389
|
+
if (cached) return cached;
|
|
390
|
+
const org = upgradeOrg(site);
|
|
391
|
+
const raw = await httpGetBytes(releasesApiUrl(org));
|
|
392
|
+
const releases = JSON.parse(raw.toString('utf8'));
|
|
393
|
+
if (!Array.isArray(releases)) throw new Error('unexpected releases payload');
|
|
394
|
+
const ver = selectLatestStable(releases);
|
|
395
|
+
const assetName = `libaria_ffi_${ver}_${ffiAssetOs()}.tar.gz`;
|
|
396
|
+
let url;
|
|
397
|
+
for (const rel of releases) {
|
|
398
|
+
const tag = String(rel.tag_name || rel.tag || '');
|
|
399
|
+
if (stripV(tag) !== ver) continue;
|
|
400
|
+
for (const asset of rel.assets || []) {
|
|
401
|
+
if (asset.name === assetName) {
|
|
402
|
+
url = asset.browser_download_url || asset.direct_asset_url;
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (url) break;
|
|
407
|
+
}
|
|
408
|
+
if (!url) throw new Error(`release asset not found: ${assetName}`);
|
|
409
|
+
const staging = path.join(ariaHome(), 'tmp', `ffi-${ver}`);
|
|
410
|
+
fs.rmSync(staging, { recursive: true, force: true });
|
|
411
|
+
fs.mkdirSync(staging, { recursive: true });
|
|
412
|
+
const archive = path.join(staging, assetName);
|
|
413
|
+
try {
|
|
414
|
+
fs.writeFileSync(archive, await httpGetBytes(url));
|
|
415
|
+
return extractFfiArchive(archive, libDir(), ffiLibName());
|
|
416
|
+
} finally {
|
|
417
|
+
fs.rmSync(staging, { recursive: true, force: true });
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
264
421
|
export class AriaEngine {
|
|
265
422
|
constructor(bundlePath) {
|
|
266
423
|
this.bundlePath = bundlePath;
|
|
@@ -268,6 +425,7 @@ export class AriaEngine {
|
|
|
268
425
|
}
|
|
269
426
|
|
|
270
427
|
static async open(modelRef, opts = {}) {
|
|
428
|
+
await ensureFfiLib(opts.site);
|
|
271
429
|
if (isLocalRef(modelRef)) return new AriaEngine(modelRef);
|
|
272
430
|
const bundle = await downloadModel(modelRef, opts);
|
|
273
431
|
return new AriaEngine(bundle);
|