@ariacompute/engine-rn 1.15.0 → 1.16.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 +6 -3
- package/package.json +4 -1
- package/src/auth.js +115 -0
- package/src/index.js +43 -4
- package/test/auth.test.cjs +106 -0
package/README.md
CHANGED
|
@@ -20,10 +20,13 @@ ModelScope; `site` defaults to `https://ariacompute.com`) into
|
|
|
20
20
|
`~/.ariacompute/models/{model}` and then loads it. Dashboard is not used. A
|
|
21
21
|
Dashboard `sk-` / `bfvk-` token is ignored for hub auth. Token is optional for
|
|
22
22
|
public models. Gated files: pass `hfToken` / `modelscopeApiToken` (same as
|
|
23
|
-
`aria-engine auth`); if omitted, reads `~/.ariacompute/config.yml`.
|
|
24
|
-
|
|
23
|
+
`aria-engine auth`); if omitted, reads `~/.ariacompute/config.yml`. Instance
|
|
24
|
+
`auth` is in-memory only (does not write that file). A valid cached bundle is
|
|
25
|
+
reused without re-downloading.
|
|
25
26
|
|
|
26
27
|
```js
|
|
27
28
|
const eng = await AriaEngine.open("gemma-4-e2b-it_q4");
|
|
28
|
-
const gated =
|
|
29
|
+
const gated = new AriaEngine();
|
|
30
|
+
gated.auth({ hf_token: "hf_..." });
|
|
31
|
+
await gated.open("gemma-4-e2b-it_q4");
|
|
29
32
|
```
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ariacompute/engine-rn",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "React Native binding for Aria Engine",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"peerDependencies": {
|
|
8
8
|
"react-native": ">=0.72.0"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test test/auth.test.cjs"
|
|
9
12
|
}
|
|
10
13
|
}
|
package/src/auth.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** Instance-level Engine auth (in-memory; does not write config.yml). */
|
|
2
|
+
|
|
3
|
+
const INTL_CLOUD = 'https://gateway.ariacompute.com';
|
|
4
|
+
const INTL_SITE = 'https://ariacompute.com';
|
|
5
|
+
const INTL_UPGRADE = 'https://github.com/ariacompute';
|
|
6
|
+
const CN_CLOUD = 'https://gateway.ariacompute.cn';
|
|
7
|
+
const CN_SITE = 'https://ariacompute.cn';
|
|
8
|
+
const CN_UPGRADE = 'https://gitee.com/ariacompute';
|
|
9
|
+
|
|
10
|
+
function defaultAuthConfig() {
|
|
11
|
+
return {
|
|
12
|
+
cloud_api_key: '',
|
|
13
|
+
cloud_url: '',
|
|
14
|
+
site_url: '',
|
|
15
|
+
upgrade_url: '',
|
|
16
|
+
hybrid_mode: 'balance',
|
|
17
|
+
hybrid_execution: 'hybrid',
|
|
18
|
+
hybrid_semantic: true,
|
|
19
|
+
hybrid_semantic_timeout_ms: 800,
|
|
20
|
+
hybrid_semantic_cache_size: 512,
|
|
21
|
+
compute: 'auto',
|
|
22
|
+
hf_token: '',
|
|
23
|
+
modelscope_api_token: '',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function gatewayRegion(url) {
|
|
28
|
+
const lower = (url || '').toLowerCase();
|
|
29
|
+
if (lower.includes('ariacompute.cn') || lower.includes('gitee.com/ariacompute')) return 'cn';
|
|
30
|
+
if (lower.includes('ariacompute.com') || lower.includes('github.com/ariacompute')) return 'intl';
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function pairUrls(region) {
|
|
35
|
+
return region === 'cn' ? [CN_CLOUD, CN_SITE, CN_UPGRADE] : [INTL_CLOUD, INTL_SITE, INTL_UPGRADE];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function fillAuthUrls(cfg) {
|
|
39
|
+
const out = { ...cfg };
|
|
40
|
+
const region =
|
|
41
|
+
gatewayRegion(out.site_url) || gatewayRegion(out.cloud_url) || gatewayRegion(out.upgrade_url);
|
|
42
|
+
if (!region) return out;
|
|
43
|
+
const [cloud, site, upgrade] = pairUrls(region);
|
|
44
|
+
if (!out.cloud_url) out.cloud_url = cloud;
|
|
45
|
+
if (!out.site_url) out.site_url = site;
|
|
46
|
+
if (!out.upgrade_url) out.upgrade_url = upgrade;
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function localePrefersCn() {
|
|
51
|
+
const lang = `${process.env.LANG || ''}${process.env.LC_ALL || ''}`.toLowerCase();
|
|
52
|
+
return lang.includes('zh') || lang.includes('.cn') || lang.startsWith('cn');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const authHooks = {
|
|
56
|
+
probeDashboard: () => false,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function detectGatewayPair(apiKey) {
|
|
60
|
+
const key = (apiKey || '').trim();
|
|
61
|
+
const first = localePrefersCn() ? 'cn' : 'intl';
|
|
62
|
+
const second = first === 'cn' ? 'intl' : 'cn';
|
|
63
|
+
for (const region of [first, second]) {
|
|
64
|
+
const [cloud, site, upgrade] = pairUrls(region);
|
|
65
|
+
if (key && authHooks.probeDashboard(site, key)) return [cloud, site, upgrade];
|
|
66
|
+
}
|
|
67
|
+
return pairUrls(first);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function applyAuth(existing, updates) {
|
|
71
|
+
const out = { ...existing };
|
|
72
|
+
for (const [k, v] of Object.entries(updates || {})) {
|
|
73
|
+
if (v === undefined) continue;
|
|
74
|
+
out[k] = v;
|
|
75
|
+
}
|
|
76
|
+
if (!['cost', 'balance', 'intelligence'].includes(out.hybrid_mode)) {
|
|
77
|
+
throw new Error(`invalid hybrid_mode: ${out.hybrid_mode}`);
|
|
78
|
+
}
|
|
79
|
+
if (!['hybrid', 'device', 'cloud'].includes(out.hybrid_execution)) {
|
|
80
|
+
throw new Error(`invalid hybrid_execution: ${out.hybrid_execution}`);
|
|
81
|
+
}
|
|
82
|
+
if (!['auto', 'cpu', 'cuda'].includes(out.compute)) {
|
|
83
|
+
throw new Error(`invalid compute: ${out.compute}`);
|
|
84
|
+
}
|
|
85
|
+
const timeout = Number(out.hybrid_semantic_timeout_ms);
|
|
86
|
+
const cache = Number(out.hybrid_semantic_cache_size);
|
|
87
|
+
if (!Number.isInteger(timeout) || !Number.isInteger(cache) || timeout <= 0 || cache <= 0) {
|
|
88
|
+
throw new Error('hybrid_semantic_timeout_ms / cache_size must be positive integers');
|
|
89
|
+
}
|
|
90
|
+
out.hybrid_semantic = Boolean(out.hybrid_semantic);
|
|
91
|
+
out.hybrid_semantic_timeout_ms = timeout;
|
|
92
|
+
out.hybrid_semantic_cache_size = cache;
|
|
93
|
+
const filled = fillAuthUrls(out);
|
|
94
|
+
if (filled.cloud_api_key && !(filled.cloud_url && filled.site_url && filled.upgrade_url)) {
|
|
95
|
+
const [cloud, site, upgrade] = detectGatewayPair(filled.cloud_api_key);
|
|
96
|
+
if (!filled.cloud_url) filled.cloud_url = cloud;
|
|
97
|
+
if (!filled.site_url) filled.site_url = site;
|
|
98
|
+
if (!filled.upgrade_url) filled.upgrade_url = upgrade;
|
|
99
|
+
}
|
|
100
|
+
return filled;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
INTL_CLOUD,
|
|
105
|
+
INTL_SITE,
|
|
106
|
+
INTL_UPGRADE,
|
|
107
|
+
CN_CLOUD,
|
|
108
|
+
CN_SITE,
|
|
109
|
+
CN_UPGRADE,
|
|
110
|
+
defaultAuthConfig,
|
|
111
|
+
fillAuthUrls,
|
|
112
|
+
applyAuth,
|
|
113
|
+
authHooks,
|
|
114
|
+
detectGatewayPair,
|
|
115
|
+
};
|
package/src/index.js
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
* `~/.ariacompute/models/{model}`, then loads via native init.
|
|
9
9
|
*/
|
|
10
10
|
import { NativeModules } from 'react-native';
|
|
11
|
+
import {
|
|
12
|
+
defaultAuthConfig,
|
|
13
|
+
applyAuth,
|
|
14
|
+
} from './auth.js';
|
|
11
15
|
|
|
12
16
|
const AriaEngineModule = NativeModules.AriaEngine;
|
|
13
17
|
|
|
@@ -421,14 +425,49 @@ async function ensureFfiLib(site) {
|
|
|
421
425
|
export class AriaEngine {
|
|
422
426
|
constructor(bundlePath) {
|
|
423
427
|
this.bundlePath = bundlePath;
|
|
428
|
+
this.cfg = defaultAuthConfig();
|
|
429
|
+
this.opts = {};
|
|
424
430
|
// NativeModules.AriaEngine.init(bundlePath) when linked.
|
|
425
431
|
}
|
|
426
432
|
|
|
427
|
-
|
|
433
|
+
auth(updates) {
|
|
434
|
+
this.cfg = applyAuth(this.cfg, updates);
|
|
435
|
+
return this;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
authStatus() {
|
|
439
|
+
return { ...this.cfg };
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
authClear() {
|
|
443
|
+
this.cfg = defaultAuthConfig();
|
|
444
|
+
return this;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async open(modelRef) {
|
|
448
|
+
const opts = {
|
|
449
|
+
...this.opts,
|
|
450
|
+
site: this.cfg.site_url || this.opts.site,
|
|
451
|
+
hfToken: this.cfg.hf_token || this.opts.hfToken,
|
|
452
|
+
modelscopeApiToken: this.cfg.modelscope_api_token || this.opts.modelscopeApiToken,
|
|
453
|
+
};
|
|
428
454
|
await ensureFfiLib(opts.site);
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
455
|
+
this.bundlePath = isLocalRef(modelRef) ? modelRef : await downloadModel(modelRef, opts);
|
|
456
|
+
return this;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
static async open(modelRef, opts = {}) {
|
|
460
|
+
const eng = new AriaEngine();
|
|
461
|
+
eng.opts = opts;
|
|
462
|
+
if (opts.site || opts.hfToken || opts.modelscopeApiToken) {
|
|
463
|
+
eng.auth({
|
|
464
|
+
site_url: opts.site,
|
|
465
|
+
hf_token: opts.hfToken,
|
|
466
|
+
modelscope_api_token: opts.modelscopeApiToken,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
await eng.open(modelRef);
|
|
470
|
+
return eng;
|
|
432
471
|
}
|
|
433
472
|
|
|
434
473
|
async complete(messages, options = { max_tokens: 16 }, tools = []) {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const test = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const {
|
|
7
|
+
applyAuth,
|
|
8
|
+
defaultAuthConfig,
|
|
9
|
+
fillAuthUrls,
|
|
10
|
+
authHooks,
|
|
11
|
+
CN_CLOUD,
|
|
12
|
+
CN_SITE,
|
|
13
|
+
CN_UPGRADE,
|
|
14
|
+
} = require('../src/auth.js');
|
|
15
|
+
|
|
16
|
+
test('auth defaults', () => {
|
|
17
|
+
const cfg = defaultAuthConfig();
|
|
18
|
+
assert.equal(cfg.hybrid_mode, 'balance');
|
|
19
|
+
assert.equal(cfg.hybrid_execution, 'hybrid');
|
|
20
|
+
assert.equal(cfg.hybrid_semantic, true);
|
|
21
|
+
assert.equal(cfg.hybrid_semantic_timeout_ms, 800);
|
|
22
|
+
assert.equal(cfg.hybrid_semantic_cache_size, 512);
|
|
23
|
+
assert.equal(cfg.compute, 'auto');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('auth invalid enum', () => {
|
|
27
|
+
assert.throws(() => applyAuth(defaultAuthConfig(), { hybrid_mode: 'fast' }));
|
|
28
|
+
assert.throws(() => applyAuth(defaultAuthConfig(), { hybrid_execution: 'local' }));
|
|
29
|
+
assert.throws(() => applyAuth(defaultAuthConfig(), { compute: 'gpu' }));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('auth fills urls from cn site', () => {
|
|
33
|
+
const got = fillAuthUrls({ site_url: CN_SITE, cloud_url: '', upgrade_url: '' });
|
|
34
|
+
assert.equal(got.cloud_url, CN_CLOUD);
|
|
35
|
+
assert.equal(got.upgrade_url, CN_UPGRADE);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('auth instance all fields via applyAuth', () => {
|
|
39
|
+
const st = applyAuth(defaultAuthConfig(), {
|
|
40
|
+
cloud_api_key: 'sk-test',
|
|
41
|
+
cloud_url: CN_CLOUD,
|
|
42
|
+
site_url: CN_SITE,
|
|
43
|
+
upgrade_url: CN_UPGRADE,
|
|
44
|
+
hybrid_mode: 'cost',
|
|
45
|
+
hybrid_execution: 'device',
|
|
46
|
+
hybrid_semantic: false,
|
|
47
|
+
hybrid_semantic_timeout_ms: 250,
|
|
48
|
+
hybrid_semantic_cache_size: 16,
|
|
49
|
+
compute: 'cpu',
|
|
50
|
+
hf_token: 'hf_abc',
|
|
51
|
+
modelscope_api_token: 'ms_xyz',
|
|
52
|
+
});
|
|
53
|
+
assert.equal(st.cloud_api_key, 'sk-test');
|
|
54
|
+
assert.equal(st.hybrid_mode, 'cost');
|
|
55
|
+
assert.equal(st.hf_token, 'hf_abc');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('auth partial merge', () => {
|
|
59
|
+
const once = applyAuth(defaultAuthConfig(), { hf_token: 'hf_one', hybrid_mode: 'intelligence' });
|
|
60
|
+
const st = applyAuth(once, { compute: 'cuda' });
|
|
61
|
+
assert.equal(st.hf_token, 'hf_one');
|
|
62
|
+
assert.equal(st.hybrid_mode, 'intelligence');
|
|
63
|
+
assert.equal(st.compute, 'cuda');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('auth invalid enum leaves state', () => {
|
|
67
|
+
const once = applyAuth(defaultAuthConfig(), { hybrid_mode: 'cost' });
|
|
68
|
+
assert.throws(() => applyAuth(once, { hybrid_mode: 'nope' }));
|
|
69
|
+
assert.equal(once.hybrid_mode, 'cost');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('auth fills urls from site tld', () => {
|
|
73
|
+
const st = applyAuth(defaultAuthConfig(), { site_url: 'https://ariacompute.cn' });
|
|
74
|
+
assert.equal(st.cloud_url, CN_CLOUD);
|
|
75
|
+
assert.equal(st.upgrade_url, CN_UPGRADE);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('auth does not write config.yml', () => {
|
|
79
|
+
const prev = process.env.ARIA_COMPUTE_HOME;
|
|
80
|
+
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'aria-rn-auth-'));
|
|
81
|
+
process.env.ARIA_COMPUTE_HOME = home;
|
|
82
|
+
try {
|
|
83
|
+
applyAuth(defaultAuthConfig(), {
|
|
84
|
+
cloud_api_key: 'sk-test',
|
|
85
|
+
site_url: 'https://ariacompute.com',
|
|
86
|
+
hf_token: 'hf_x',
|
|
87
|
+
});
|
|
88
|
+
assert.equal(fs.existsSync(path.join(home, 'config.yml')), false);
|
|
89
|
+
} finally {
|
|
90
|
+
if (prev === undefined) delete process.env.ARIA_COMPUTE_HOME;
|
|
91
|
+
else process.env.ARIA_COMPUTE_HOME = prev;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('auth detect urls from key mocked', () => {
|
|
96
|
+
const prev = authHooks.probeDashboard;
|
|
97
|
+
authHooks.probeDashboard = (site) => String(site).includes('ariacompute.cn');
|
|
98
|
+
try {
|
|
99
|
+
const st = applyAuth(defaultAuthConfig(), { cloud_api_key: 'sk-region' });
|
|
100
|
+
assert.equal(st.site_url, CN_SITE);
|
|
101
|
+
assert.equal(st.cloud_url, CN_CLOUD);
|
|
102
|
+
assert.equal(st.upgrade_url, CN_UPGRADE);
|
|
103
|
+
} finally {
|
|
104
|
+
authHooks.probeDashboard = prev;
|
|
105
|
+
}
|
|
106
|
+
});
|