termux-vision 1.0.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/LICENSE +176 -0
- package/NOTICE +17 -0
- package/README.md +151 -0
- package/bin/cli.js +352 -0
- package/index.d.ts +97 -0
- package/index.js +29 -0
- package/lib/cache.js +300 -0
- package/lib/cv.js +54 -0
- package/lib/detect.js +47 -0
- package/lib/doctor.js +77 -0
- package/lib/errors.js +119 -0
- package/lib/vlm.js +291 -0
- package/package.json +55 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* termux-vision: Node.js Global CLI Binary.
|
|
5
|
+
* High-performance on-device Computer Vision & VLM Multimodal Framework.
|
|
6
|
+
* Open-Source under Apache License 2.0.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const readline = require('readline');
|
|
15
|
+
|
|
16
|
+
const { version } = require('../package.json');
|
|
17
|
+
const { runDoctor } = require('../lib/doctor');
|
|
18
|
+
const { ModelCacheManager, CATALOG } = require('../lib/cache');
|
|
19
|
+
const { load } = require('../lib/vlm');
|
|
20
|
+
const { canny } = require('../lib/cv');
|
|
21
|
+
const {
|
|
22
|
+
ModelNotFoundError,
|
|
23
|
+
NoInstalledModelsError,
|
|
24
|
+
ModelSelectionRequiredError,
|
|
25
|
+
VulkanNotAvailableError,
|
|
26
|
+
RuntimeNotFoundError,
|
|
27
|
+
ModelDownloadError
|
|
28
|
+
} = require('../lib/errors');
|
|
29
|
+
|
|
30
|
+
const args = process.argv.slice(2);
|
|
31
|
+
const command = args[0] || '--help';
|
|
32
|
+
|
|
33
|
+
function getArg(flag, alias = null) {
|
|
34
|
+
let idx = args.indexOf(flag);
|
|
35
|
+
if (idx === -1 && alias) idx = args.indexOf(alias);
|
|
36
|
+
return idx !== -1 && args[idx + 1] ? args[idx + 1] : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function hasFlag(flag, alias = null) {
|
|
40
|
+
return args.includes(flag) || (alias && args.includes(alias));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function printHelp() {
|
|
44
|
+
console.log(`termux-vision CLI v${version} (Node.js Engine)`);
|
|
45
|
+
console.log('Usage: termux-vision <command> [options]\n');
|
|
46
|
+
console.log('Commands:');
|
|
47
|
+
console.log(' doctor Inspect device hardware, RAM, and Vulkan GPU');
|
|
48
|
+
console.log(' model list List installed VLM models');
|
|
49
|
+
console.log(' model install <model_id> Download and install official VLM model');
|
|
50
|
+
console.log(' model download <url_or_repo> Freely download model from Hugging Face or direct URL');
|
|
51
|
+
console.log(' model remove <model_id> Remove model from cache');
|
|
52
|
+
console.log(' vlm <image_path> [options] Execute multimodal image description/chat');
|
|
53
|
+
console.log(' canny <image_path> [options] Run Canny edge detection');
|
|
54
|
+
console.log(' benchmark Run on-device vision latency benchmark\n');
|
|
55
|
+
console.log('Options for VLM:');
|
|
56
|
+
console.log(' -p, --prompt <text> Prompt query');
|
|
57
|
+
console.log(' -m, --model <model_id_or_path> Model ID or direct path to .gguf file');
|
|
58
|
+
console.log(' --mmproj <path> Vision projector path (mmproj-*.gguf)');
|
|
59
|
+
console.log(' --device <auto|cpu|vulkan|gpu> Device backend (auto: Vulkan with CPU fallback; gpu: strict Vulkan)');
|
|
60
|
+
console.log(' --runtime <path> Explicit path to llama-cli executable');
|
|
61
|
+
console.log(' --allow-download Automatically download model if missing');
|
|
62
|
+
console.log(' -t, --threads <num> Inference threads (default: 4)');
|
|
63
|
+
console.log(' -n, --max-tokens <num> Maximum generated tokens (default: 150)');
|
|
64
|
+
console.log(' --temp, --temperature <val> Sampling temperature (default: 0.2)');
|
|
65
|
+
console.log(' --top-p <val> Top-p nucleus sampling');
|
|
66
|
+
console.log(' --top-k <num> Top-k sampling threshold');
|
|
67
|
+
console.log(' --repeat-penalty <val> Repetition penalty (default: 1.2)');
|
|
68
|
+
console.log(' --seed <num> Random RNG seed');
|
|
69
|
+
console.log(' --system-prompt <text> System prompt context');
|
|
70
|
+
console.log(' --ngl <num> Number of GPU offload layers');
|
|
71
|
+
console.log(' --json Output full metrics in JSON format');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function promptUser(question) {
|
|
75
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
rl.question(question, (ans) => {
|
|
78
|
+
rl.close();
|
|
79
|
+
resolve(ans.trim().toLowerCase());
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function main() {
|
|
85
|
+
if (hasFlag('-v') || hasFlag('--version')) {
|
|
86
|
+
console.log(`termux-vision ${version}`);
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (command === '--help' || command === '-h' || command === 'help') {
|
|
91
|
+
printHelp();
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const cache = new ModelCacheManager();
|
|
96
|
+
|
|
97
|
+
if (command === 'doctor') {
|
|
98
|
+
const probeVulkan = hasFlag('--probe-vulkan');
|
|
99
|
+
const isJson = hasFlag('--json');
|
|
100
|
+
const rep = runDoctor(probeVulkan);
|
|
101
|
+
|
|
102
|
+
if (isJson) {
|
|
103
|
+
console.log(JSON.stringify(rep, null, 2));
|
|
104
|
+
} else {
|
|
105
|
+
console.log('=== termux-vision Diagnostic Doctor (Node.js Engine) ===');
|
|
106
|
+
console.log(` Platform : ${rep.platform.system} (${rep.platform.machine}) | Android: ${rep.platform.isAndroid}`);
|
|
107
|
+
console.log(` RAM : Total ${rep.hardware.totalRamMb}MB | Available ${rep.hardware.availableRamMb}MB`);
|
|
108
|
+
console.log(` CPU Cores: ${rep.hardware.cpuCores}`);
|
|
109
|
+
console.log(` Vulkan : Loader=${rep.vulkan.loaderDetected} | Driver=${rep.vulkan.driverDetected} | Status=${rep.vulkan.status}`);
|
|
110
|
+
console.log(` Models : ${rep.vlmRuntime.installedModelsCount} installed in ${rep.vlmRuntime.cacheDir}`);
|
|
111
|
+
console.log(` Preset : ${rep.recommendedPreset}`);
|
|
112
|
+
}
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (command === 'model') {
|
|
117
|
+
const action = args[1] || 'list';
|
|
118
|
+
if (action === 'list') {
|
|
119
|
+
const isJson = hasFlag('--json');
|
|
120
|
+
const installed = cache.listInstalled();
|
|
121
|
+
if (isJson) {
|
|
122
|
+
console.log(JSON.stringify(installed, null, 2));
|
|
123
|
+
} else {
|
|
124
|
+
console.log(`=== Installed VLM Models (${installed.length}) ===`);
|
|
125
|
+
if (installed.length === 0) {
|
|
126
|
+
console.log(' (none in ~/.cache/termux-vision/models)');
|
|
127
|
+
} else {
|
|
128
|
+
for (const m of installed) {
|
|
129
|
+
console.log(` - ${m.modelId.padEnd(20)} | Tier: ${m.tier} | State: ${m.state} | Size: ${m.sizeMb}MB`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
console.log('\nAvailable Official Presets:');
|
|
133
|
+
for (const [k, v] of Object.entries(CATALOG)) {
|
|
134
|
+
console.log(` * ${k.padEnd(20)} | Tier: ${v.tier} | Est. RAM: ${v.estimatedMemoryMb}MB`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
process.exit(0);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (action === 'install') {
|
|
141
|
+
const modelId = args[2];
|
|
142
|
+
if (!modelId) {
|
|
143
|
+
console.error('[ERROR] Please specify a model ID to install (e.g. smolvlm-500m-q4).');
|
|
144
|
+
process.exit(2);
|
|
145
|
+
}
|
|
146
|
+
console.log(`[*] Installing model: ${modelId}...`);
|
|
147
|
+
try {
|
|
148
|
+
await cache.install(modelId, (fname, downloaded, total) => {
|
|
149
|
+
const pct = total > 0 ? (downloaded / total * 100).toFixed(1) : '0.0';
|
|
150
|
+
process.stdout.write(`\r Downloading ${fname}: ${(downloaded / 1048576).toFixed(1)}/${(total / 1048576).toFixed(1)}MB (${pct}%)`);
|
|
151
|
+
});
|
|
152
|
+
console.log(`\n[+] Successfully installed '${modelId}'.`);
|
|
153
|
+
process.exit(0);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.error(`\n[-] Model installation failed: ${err.message}`);
|
|
156
|
+
process.exit(11);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (action === 'download') {
|
|
161
|
+
const source = args[2];
|
|
162
|
+
if (!source) {
|
|
163
|
+
console.error('[ERROR] Please provide a Hugging Face repo or direct URL.');
|
|
164
|
+
process.exit(2);
|
|
165
|
+
}
|
|
166
|
+
const outDir = getArg('-o', '--output') || cache.modelsDir;
|
|
167
|
+
console.log(`[*] Downloading model from: ${source}...`);
|
|
168
|
+
try {
|
|
169
|
+
let dest = path.join(outDir, path.basename(source.split('?')[0]));
|
|
170
|
+
if (source.startsWith('hf:') || source.includes(':')) {
|
|
171
|
+
const parts = (source.startsWith('hf:') ? source.slice(3) : source).split(':');
|
|
172
|
+
if (parts.length === 2) {
|
|
173
|
+
const url = `https://huggingface.co/${parts[0].trim()}/resolve/main/${parts[1].trim()}`;
|
|
174
|
+
dest = path.join(outDir, parts[1].trim());
|
|
175
|
+
await cache.downloadFile(url, dest, (fname, d, t) => {
|
|
176
|
+
const pct = t > 0 ? (d / t * 100).toFixed(1) : '0.0';
|
|
177
|
+
process.stdout.write(`\r Downloading ${fname}: ${(d / 1048576).toFixed(1)}/${(t / 1048576).toFixed(1)}MB (${pct}%)`);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
} else if (source.startsWith('http')) {
|
|
181
|
+
await cache.downloadFile(source, dest, (fname, d, t) => {
|
|
182
|
+
const pct = t > 0 ? (d / t * 100).toFixed(1) : '0.0';
|
|
183
|
+
process.stdout.write(`\r Downloading ${fname}: ${(d / 1048576).toFixed(1)}/${(t / 1048576).toFixed(1)}MB (${pct}%)`);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
console.log(`\n[+] Model downloaded successfully to: ${dest}`);
|
|
187
|
+
process.exit(0);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
console.error(`\n[-] Model download failed: ${err.message}`);
|
|
190
|
+
process.exit(11);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (action === 'remove') {
|
|
195
|
+
const modelId = args[2];
|
|
196
|
+
if (!modelId) {
|
|
197
|
+
console.error('[ERROR] Please specify a model ID to remove.');
|
|
198
|
+
process.exit(2);
|
|
199
|
+
}
|
|
200
|
+
if (cache.remove(modelId)) {
|
|
201
|
+
console.log(`[+] Model '${modelId}' removed from cache.`);
|
|
202
|
+
process.exit(0);
|
|
203
|
+
} else {
|
|
204
|
+
console.error(`[-] Model '${modelId}' was not found in cache.`);
|
|
205
|
+
process.exit(10);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (command === 'vlm') {
|
|
211
|
+
const imagePath = args[1];
|
|
212
|
+
if (!imagePath || imagePath.startsWith('-')) {
|
|
213
|
+
console.error('[ERROR] Missing input image path for VLM inference.');
|
|
214
|
+
console.error('Usage: termux-vision vlm <image_path> [options]');
|
|
215
|
+
process.exit(2);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const prompt = getArg('-p', '--prompt');
|
|
219
|
+
const model = getArg('-m', '--model');
|
|
220
|
+
const mmproj = getArg('--mmproj', null);
|
|
221
|
+
const device = getArg('--device', null) || 'auto';
|
|
222
|
+
const runtime = getArg('--runtime', null);
|
|
223
|
+
const allowDownload = hasFlag('--allow-download');
|
|
224
|
+
const threads = getArg('-t', '--threads');
|
|
225
|
+
const maxTokens = getArg('-n', '--max-tokens');
|
|
226
|
+
const temp = getArg('--temp', '--temperature');
|
|
227
|
+
const topP = getArg('--top-p', null);
|
|
228
|
+
const topK = getArg('--top-k', null);
|
|
229
|
+
const repeatPenalty = getArg('--repeat-penalty', null);
|
|
230
|
+
const seed = getArg('--seed', null);
|
|
231
|
+
const systemPrompt = getArg('--system-prompt', null);
|
|
232
|
+
const ngl = getArg('--ngl', null);
|
|
233
|
+
const isJson = hasFlag('--json');
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
let targetModel = model;
|
|
237
|
+
if (!targetModel) {
|
|
238
|
+
const installed = cache.listInstalled();
|
|
239
|
+
if (installed.length === 0) {
|
|
240
|
+
if (allowDownload) {
|
|
241
|
+
targetModel = 'smolvlm-500m-q4';
|
|
242
|
+
} else if (process.stdin.isTTY) {
|
|
243
|
+
console.error('\n---------------------------------------------------------');
|
|
244
|
+
console.error(' [Notice] No local VLM model is currently installed.');
|
|
245
|
+
console.error(' Default Model : smolvlm-500m-q4 (SmolVLM 500M Instruct)');
|
|
246
|
+
console.error(' Download Size : ~550 MB');
|
|
247
|
+
console.error(' Target Path : ~/.cache/termux-vision/models/smolvlm-500m-q4/');
|
|
248
|
+
console.error('---------------------------------------------------------');
|
|
249
|
+
const ans = await promptUser('Do you want to download and install this model now? [y/N]: ');
|
|
250
|
+
if (ans === 'y' || ans === 'yes') {
|
|
251
|
+
console.error('[*] Downloading smolvlm-500m-q4 (~550MB)...');
|
|
252
|
+
await cache.install('smolvlm-500m-q4');
|
|
253
|
+
console.error('[+] Successfully installed smolvlm-500m-q4.');
|
|
254
|
+
targetModel = 'smolvlm-500m-q4';
|
|
255
|
+
} else {
|
|
256
|
+
throw new NoInstalledModelsError(cache.getAvailableCatalogModels());
|
|
257
|
+
}
|
|
258
|
+
} else {
|
|
259
|
+
throw new NoInstalledModelsError(cache.getAvailableCatalogModels());
|
|
260
|
+
}
|
|
261
|
+
} else if (installed.length === 1) {
|
|
262
|
+
targetModel = installed[0].modelId;
|
|
263
|
+
console.error(`[INFO] Selected installed model: ${targetModel}`);
|
|
264
|
+
} else {
|
|
265
|
+
throw new ModelSelectionRequiredError(installed.map(m => m.modelId));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const engine = await load({
|
|
270
|
+
modelId: targetModel,
|
|
271
|
+
mmprojPath: mmproj,
|
|
272
|
+
device: device,
|
|
273
|
+
runtimePath: runtime,
|
|
274
|
+
allowDownload: allowDownload,
|
|
275
|
+
threads: threads ? parseInt(threads, 10) : 4,
|
|
276
|
+
ngl: ngl ? parseInt(ngl, 10) : null
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const res = await engine.describe(imagePath, {
|
|
280
|
+
prompt: prompt,
|
|
281
|
+
maxTokens: maxTokens ? parseInt(maxTokens, 10) : 150,
|
|
282
|
+
temperature: temp ? parseFloat(temp) : 0.2,
|
|
283
|
+
topP: topP ? parseFloat(topP) : undefined,
|
|
284
|
+
topK: topK ? parseInt(topK, 10) : undefined,
|
|
285
|
+
repeatPenalty: repeatPenalty ? parseFloat(repeatPenalty) : undefined,
|
|
286
|
+
seed: seed ? parseInt(seed, 10) : undefined,
|
|
287
|
+
systemPrompt: systemPrompt || undefined
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (isJson) {
|
|
291
|
+
console.log(JSON.stringify(res, null, 2));
|
|
292
|
+
} else {
|
|
293
|
+
if (res.warnings && res.warnings.length > 0) {
|
|
294
|
+
for (const w of res.warnings) {
|
|
295
|
+
console.error(`[WARNING] ${w}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const tpsStr = res.metrics.tokensPerSecond ? ` | ${res.metrics.tokensPerSecond.toFixed(1)} t/s` : '';
|
|
299
|
+
console.log(`\n[VLM Result | backend=${res.metrics.backend}${tpsStr}]`);
|
|
300
|
+
console.log(res.text);
|
|
301
|
+
}
|
|
302
|
+
process.exit(0);
|
|
303
|
+
} catch (err) {
|
|
304
|
+
if (err instanceof VulkanNotAvailableError) {
|
|
305
|
+
console.error(`[ERROR] ${err.message}`);
|
|
306
|
+
process.exit(24);
|
|
307
|
+
} else if (err instanceof RuntimeNotFoundError) {
|
|
308
|
+
console.error(`[ERROR] ${err.message}`);
|
|
309
|
+
process.exit(20);
|
|
310
|
+
} else if (err instanceof NoInstalledModelsError) {
|
|
311
|
+
console.error(`[ERROR] ${err.message}`);
|
|
312
|
+
process.exit(21);
|
|
313
|
+
} else if (err instanceof ModelSelectionRequiredError) {
|
|
314
|
+
console.error(`[ERROR] ${err.message}`);
|
|
315
|
+
process.exit(23);
|
|
316
|
+
} else if (err instanceof ModelNotFoundError) {
|
|
317
|
+
console.error(`[ERROR] ${err.message}`);
|
|
318
|
+
process.exit(10);
|
|
319
|
+
} else if (err instanceof ModelDownloadError) {
|
|
320
|
+
console.error(`[ERROR] ${err.message}`);
|
|
321
|
+
process.exit(11);
|
|
322
|
+
} else {
|
|
323
|
+
console.error(`[ERROR] VLM execution failed: ${err.message}`);
|
|
324
|
+
process.exit(15);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (command === 'benchmark') {
|
|
330
|
+
console.log('=== termux-vision On-Device Benchmark (Node.js Engine) ===');
|
|
331
|
+
const width = 256;
|
|
332
|
+
const height = 256;
|
|
333
|
+
const dummy = new Uint8Array(width * height);
|
|
334
|
+
for (let i = 0; i < dummy.length; i++) dummy[i] = Math.floor(Math.random() * 256);
|
|
335
|
+
|
|
336
|
+
const t0 = Date.now();
|
|
337
|
+
const edges = canny(dummy, width, height, 40, 120);
|
|
338
|
+
const lat = Date.now() - t0;
|
|
339
|
+
console.log(` - Canny Edge Detection (256x256): ${lat} ms`);
|
|
340
|
+
console.log('[+] Benchmark Complete.');
|
|
341
|
+
process.exit(0);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
console.error(`[ERROR] Unknown command: '${command}'`);
|
|
345
|
+
printHelp();
|
|
346
|
+
process.exit(2);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
main().catch((err) => {
|
|
350
|
+
console.error(`[FATAL] ${err.message}`);
|
|
351
|
+
process.exit(1);
|
|
352
|
+
});
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for termux-vision Node.js SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface VLMDescribeOptions {
|
|
6
|
+
prompt?: string;
|
|
7
|
+
maxTokens?: number;
|
|
8
|
+
temperature?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface InferenceMetrics {
|
|
12
|
+
backend: string;
|
|
13
|
+
modelId: string;
|
|
14
|
+
decodeMs: number;
|
|
15
|
+
tokensPerSecond?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface VLMResult {
|
|
19
|
+
text: string;
|
|
20
|
+
finishReason: string;
|
|
21
|
+
wordCount: number;
|
|
22
|
+
metrics: InferenceMetrics;
|
|
23
|
+
warnings: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface VLMContext {
|
|
27
|
+
describe(imagePath: string, options?: VLMDescribeOptions): Promise<VLMResult>;
|
|
28
|
+
ask(imagePath: string, question: string): Promise<string>;
|
|
29
|
+
close(): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface LoadOptions {
|
|
33
|
+
modelId?: string;
|
|
34
|
+
device?: 'auto' | 'cpu' | 'vulkan' | 'gpu' | 'vulkan-force';
|
|
35
|
+
threads?: number | 'auto';
|
|
36
|
+
runtimePath?: string;
|
|
37
|
+
mmprojPath?: string;
|
|
38
|
+
allowDownload?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface DiagnosticReport {
|
|
42
|
+
schemaVersion: number;
|
|
43
|
+
platform: {
|
|
44
|
+
system: string;
|
|
45
|
+
machine: string;
|
|
46
|
+
isAndroid: boolean;
|
|
47
|
+
};
|
|
48
|
+
hardware: {
|
|
49
|
+
cpuCores: number;
|
|
50
|
+
totalRamMb: number | null;
|
|
51
|
+
availableRamMb: number | null;
|
|
52
|
+
};
|
|
53
|
+
vulkan: {
|
|
54
|
+
loaderDetected: boolean;
|
|
55
|
+
driverDetected: boolean;
|
|
56
|
+
status: string;
|
|
57
|
+
};
|
|
58
|
+
modelsCount: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ModelInfo {
|
|
62
|
+
modelId: string;
|
|
63
|
+
tier: string;
|
|
64
|
+
state: string;
|
|
65
|
+
sizeMb: number;
|
|
66
|
+
path: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export namespace vlm {
|
|
70
|
+
export function load(options?: LoadOptions): Promise<VLMContext>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export namespace cache {
|
|
74
|
+
export function listInstalled(): ModelInfo[];
|
|
75
|
+
export function install(modelId: string, onProgress?: (file: string, downloaded: number, total: number) => void): Promise<void>;
|
|
76
|
+
export function remove(modelId: string): boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export namespace doctor {
|
|
80
|
+
export function run(probeVulkan?: boolean): DiagnosticReport;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export namespace cv {
|
|
84
|
+
export function canny(grayPixels: Uint8Array, width: number, height: number, low?: number, high?: number): Uint8Array;
|
|
85
|
+
export function sobel(grayPixels: Uint8Array, width: number, height: number): { gradX: Float32Array; gradY: Float32Array; magnitude: Float32Array };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export namespace detect {
|
|
89
|
+
export interface BBox {
|
|
90
|
+
x: number;
|
|
91
|
+
y: number;
|
|
92
|
+
width: number;
|
|
93
|
+
height: number;
|
|
94
|
+
score?: number;
|
|
95
|
+
}
|
|
96
|
+
export function nms(boxes: BBox[], iouThreshold?: number): BBox[];
|
|
97
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* termux-vision: Native On-Device Computer Vision & VLM Multimodal Inference Framework.
|
|
3
|
+
* Dual-Engine (Python & Node.js/TypeScript) Native Module for Android Termux & ARM64.
|
|
4
|
+
* Open-Source under Apache License 2.0.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const errors = require('./lib/errors');
|
|
10
|
+
const cache = require('./lib/cache');
|
|
11
|
+
const vlm = require('./lib/vlm');
|
|
12
|
+
const doctor = require('./lib/doctor');
|
|
13
|
+
const cv = require('./lib/cv');
|
|
14
|
+
const detect = require('./lib/detect');
|
|
15
|
+
|
|
16
|
+
const packageJson = require('./package.json');
|
|
17
|
+
const version = packageJson.version;
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
version,
|
|
21
|
+
__version__: version,
|
|
22
|
+
errors,
|
|
23
|
+
cache,
|
|
24
|
+
vlm,
|
|
25
|
+
load: vlm.load,
|
|
26
|
+
doctor: doctor.runDoctor,
|
|
27
|
+
cv,
|
|
28
|
+
detect
|
|
29
|
+
};
|