@lloyal-labs/lloyal.node 1.0.3-alpha
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 +201 -0
- package/README.md +356 -0
- package/lib/index.d.ts +1388 -0
- package/lib/index.js +121 -0
- package/package.json +76 -0
- package/scripts/build.js +51 -0
- package/scripts/create-platform-package.js +152 -0
- package/scripts/download-test-models.sh +30 -0
- package/scripts/init-submodules.js +47 -0
- package/scripts/install.js +138 -0
- package/scripts/sync-versions.js +79 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const binary = require('node-gyp-build')(path.join(__dirname, '..'));
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* liblloyal-node - Thin N-API wrapper over liblloyal
|
|
6
|
+
*
|
|
7
|
+
* Exposes raw llama.cpp inference primitives for Node.js.
|
|
8
|
+
* Primary use case: Integration testing for tsampler.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```js
|
|
12
|
+
* const { createContext, withLogits } = require('lloyal.node');
|
|
13
|
+
*
|
|
14
|
+
* const ctx = await createContext({
|
|
15
|
+
* modelPath: './model.gguf',
|
|
16
|
+
* nCtx: 2048,
|
|
17
|
+
* nThreads: 4
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Tokenize
|
|
21
|
+
* const tokens = await ctx.tokenize("Hello world");
|
|
22
|
+
*
|
|
23
|
+
* // Decode
|
|
24
|
+
* await ctx.decode(tokens, 0);
|
|
25
|
+
*
|
|
26
|
+
* // Safe logits access (Runtime Borrow Checker pattern)
|
|
27
|
+
* const entropy = await withLogits(ctx, (logits) => {
|
|
28
|
+
* // logits is valid here - use synchronously only!
|
|
29
|
+
* return computeEntropy(logits);
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Or with native reference implementations (for testing)
|
|
33
|
+
* const nativeEntropy = ctx.computeEntropy();
|
|
34
|
+
* const token = ctx.greedySample();
|
|
35
|
+
*
|
|
36
|
+
* // Cleanup
|
|
37
|
+
* ctx.dispose();
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Safe logits access with Runtime Borrow Checker pattern
|
|
43
|
+
*
|
|
44
|
+
* Ensures logits are only accessed synchronously within the callback.
|
|
45
|
+
* The callback MUST NOT:
|
|
46
|
+
* - Store the logits reference
|
|
47
|
+
* - Return a Promise (will throw)
|
|
48
|
+
* - Call decode() (would invalidate logits)
|
|
49
|
+
*
|
|
50
|
+
* This is a "runtime borrow checker" - it prevents async mutations
|
|
51
|
+
* while you're working with borrowed logits.
|
|
52
|
+
*
|
|
53
|
+
* @template T
|
|
54
|
+
* @param {SessionContext} ctx - The session context
|
|
55
|
+
* @param {(logits: Float32Array) => T} fn - Synchronous callback that uses logits
|
|
56
|
+
* @returns {T} The result from the callback
|
|
57
|
+
* @throws {Error} If callback returns a Promise (async usage not allowed)
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```js
|
|
61
|
+
* // Safe: synchronous computation
|
|
62
|
+
* const entropy = withLogits(ctx, (logits) => {
|
|
63
|
+
* let sum = 0;
|
|
64
|
+
* for (let i = 0; i < logits.length; i++) {
|
|
65
|
+
* sum += Math.exp(logits[i]);
|
|
66
|
+
* }
|
|
67
|
+
* return Math.log(sum);
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* // ERROR: callback returns Promise (will throw)
|
|
71
|
+
* withLogits(ctx, async (logits) => {
|
|
72
|
+
* await something(); // NOT ALLOWED
|
|
73
|
+
* return logits[0];
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
function withLogits(ctx, fn) {
|
|
78
|
+
// Get logits (memoized - same buffer if called twice in same step)
|
|
79
|
+
const logits = ctx.getLogits();
|
|
80
|
+
|
|
81
|
+
// Execute user callback with logits
|
|
82
|
+
const result = fn(logits);
|
|
83
|
+
|
|
84
|
+
// Detect async usage (not allowed - logits would be invalidated)
|
|
85
|
+
if (result && typeof result.then === 'function') {
|
|
86
|
+
throw new Error(
|
|
87
|
+
'withLogits callback must be synchronous. ' +
|
|
88
|
+
'Returning a Promise is not allowed because logits become invalid after decode(). ' +
|
|
89
|
+
'Complete all logits processing synchronously within the callback.'
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
/**
|
|
98
|
+
* Create a new inference context
|
|
99
|
+
*
|
|
100
|
+
* @param {Object} options
|
|
101
|
+
* @param {string} options.modelPath - Path to .gguf model file
|
|
102
|
+
* @param {number} [options.nCtx=2048] - Context size
|
|
103
|
+
* @param {number} [options.nThreads=4] - Number of threads
|
|
104
|
+
* @returns {Promise<SessionContext>}
|
|
105
|
+
*/
|
|
106
|
+
createContext: async (options) => {
|
|
107
|
+
// For now, createContext is synchronous in C++
|
|
108
|
+
// Wrap in Promise for future async model loading
|
|
109
|
+
return binary.createContext(options);
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Safe logits access with Runtime Borrow Checker pattern
|
|
114
|
+
*
|
|
115
|
+
* Ensures logits are only accessed synchronously within the callback.
|
|
116
|
+
* See function JSDoc for full documentation.
|
|
117
|
+
*/
|
|
118
|
+
withLogits,
|
|
119
|
+
|
|
120
|
+
SessionContext: binary.SessionContext
|
|
121
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lloyal-labs/lloyal.node",
|
|
3
|
+
"version": "1.0.3-alpha",
|
|
4
|
+
"description": "Node.js client for liblloyal+llama.cpp",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"gypfile": false,
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"download-models": "bash scripts/download-test-models.sh",
|
|
13
|
+
"install": "node scripts/install.js",
|
|
14
|
+
"build": "node scripts/build.js",
|
|
15
|
+
"build:debug": "cmake-js compile --debug",
|
|
16
|
+
"rebuild": "cmake-js rebuild",
|
|
17
|
+
"clean": "cmake-js clean && rm -rf build_test/",
|
|
18
|
+
"version": "node scripts/sync-versions.js && git add -A",
|
|
19
|
+
"docs": "npx typedoc",
|
|
20
|
+
"test": "npm run test:api && npm run test:e2e",
|
|
21
|
+
"test:api": "node test/api.js",
|
|
22
|
+
"test:e2e": "node test/e2e.js",
|
|
23
|
+
"example": "node examples/chat/chat.mjs"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/lloyal-ai/lloyal.node.git"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"llama",
|
|
31
|
+
"llama.cpp",
|
|
32
|
+
"liblloyal",
|
|
33
|
+
"napi",
|
|
34
|
+
"native",
|
|
35
|
+
"inference",
|
|
36
|
+
"llm"
|
|
37
|
+
],
|
|
38
|
+
"author": "lloyal.ai",
|
|
39
|
+
"license": "Apache-2.0",
|
|
40
|
+
"type": "commonjs",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/lloyal-ai/lloyal.node/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/lloyal-ai/lloyal.node#readme",
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"node-addon-api": "^8.5.0",
|
|
47
|
+
"node-gyp-build": "^4.8.4"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"cmake-js": "^7.4.0",
|
|
51
|
+
"glob": "^11.0.0",
|
|
52
|
+
"typedoc": "^0.27.5"
|
|
53
|
+
},
|
|
54
|
+
"optionalDependencies": {
|
|
55
|
+
"@lloyal-labs/lloyal.node-darwin-arm64": "1.0.3-alpha",
|
|
56
|
+
"@lloyal-labs/lloyal.node-darwin-x64": "1.0.3-alpha",
|
|
57
|
+
"@lloyal-labs/lloyal.node-linux-arm64": "1.0.3-alpha",
|
|
58
|
+
"@lloyal-labs/lloyal.node-linux-arm64-cuda": "1.0.3-alpha",
|
|
59
|
+
"@lloyal-labs/lloyal.node-linux-arm64-vulkan": "1.0.3-alpha",
|
|
60
|
+
"@lloyal-labs/lloyal.node-linux-x64": "1.0.3-alpha",
|
|
61
|
+
"@lloyal-labs/lloyal.node-linux-x64-cuda": "1.0.3-alpha",
|
|
62
|
+
"@lloyal-labs/lloyal.node-linux-x64-vulkan": "1.0.3-alpha",
|
|
63
|
+
"@lloyal-labs/lloyal.node-win32-arm64": "1.0.3-alpha",
|
|
64
|
+
"@lloyal-labs/lloyal.node-win32-arm64-vulkan": "1.0.3-alpha",
|
|
65
|
+
"@lloyal-labs/lloyal.node-win32-x64": "1.0.3-alpha",
|
|
66
|
+
"@lloyal-labs/lloyal.node-win32-x64-cuda": "1.0.3-alpha",
|
|
67
|
+
"@lloyal-labs/lloyal.node-win32-x64-vulkan": "1.0.3-alpha"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=22.0.0"
|
|
71
|
+
},
|
|
72
|
+
"files": [
|
|
73
|
+
"lib/",
|
|
74
|
+
"scripts/"
|
|
75
|
+
]
|
|
76
|
+
}
|
package/scripts/build.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Build script for lloyal.node
|
|
4
|
+
*
|
|
5
|
+
* Wraps cmake-js with GPU backend detection from LLOYAL_GPU environment variable.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npm run build # CPU/Metal (auto-detected)
|
|
9
|
+
* LLOYAL_GPU=cuda npm run build # CUDA
|
|
10
|
+
* LLOYAL_GPU=vulkan npm run build # Vulkan
|
|
11
|
+
* LLOYAL_GPU=metal npm run build # Metal (macOS only)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { execSync } = require('child_process');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
|
|
17
|
+
const PLATFORM = process.platform;
|
|
18
|
+
const gpuBackend = process.env.LLOYAL_GPU?.toLowerCase();
|
|
19
|
+
|
|
20
|
+
// Build cmake-js command with appropriate flags
|
|
21
|
+
const cmakeFlags = [];
|
|
22
|
+
|
|
23
|
+
if (gpuBackend === 'cuda') {
|
|
24
|
+
cmakeFlags.push('--CDGGML_CUDA=ON');
|
|
25
|
+
console.log('[lloyal.node] GPU backend: CUDA');
|
|
26
|
+
} else if (gpuBackend === 'vulkan') {
|
|
27
|
+
cmakeFlags.push('--CDGGML_VULKAN=ON');
|
|
28
|
+
console.log('[lloyal.node] GPU backend: Vulkan');
|
|
29
|
+
} else if (gpuBackend === 'metal') {
|
|
30
|
+
cmakeFlags.push('--CDGGML_METAL=ON');
|
|
31
|
+
console.log('[lloyal.node] GPU backend: Metal');
|
|
32
|
+
} else if (PLATFORM === 'darwin') {
|
|
33
|
+
// Metal is auto-enabled on macOS by llama.cpp
|
|
34
|
+
console.log('[lloyal.node] GPU backend: Metal (auto-enabled on macOS)');
|
|
35
|
+
} else {
|
|
36
|
+
console.log('[lloyal.node] GPU backend: CPU only');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const buildCmd = `npx cmake-js compile ${cmakeFlags.join(' ')}`.trim();
|
|
40
|
+
console.log(`[lloyal.node] Running: ${buildCmd}`);
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
execSync(buildCmd, {
|
|
44
|
+
cwd: __dirname + '/..',
|
|
45
|
+
stdio: 'inherit'
|
|
46
|
+
});
|
|
47
|
+
console.log('[lloyal.node] ✅ Build successful!');
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('[lloyal.node] ❌ Build failed');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Create platform-specific package for prebuilt binaries
|
|
4
|
+
*
|
|
5
|
+
* Usage: node scripts/create-platform-package.js <package-name> <os> <arch>
|
|
6
|
+
* Example: node scripts/create-platform-package.js darwin-arm64 macos-14 arm64
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const [packageName, osRunner, arch] = process.argv.slice(2);
|
|
13
|
+
|
|
14
|
+
if (!packageName || !osRunner || !arch) {
|
|
15
|
+
console.error('Usage: node create-platform-package.js <package-name> <os-runner> <arch>');
|
|
16
|
+
console.error('Example: node create-platform-package.js darwin-arm64 macos-14 arm64');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const ROOT = path.join(__dirname, '..');
|
|
21
|
+
const BUILD_DIR = path.join(ROOT, 'build', 'Release');
|
|
22
|
+
const PACKAGES_DIR = path.join(ROOT, 'packages');
|
|
23
|
+
const PKG_DIR = path.join(PACKAGES_DIR, packageName);
|
|
24
|
+
const BIN_DIR = path.join(PKG_DIR, 'bin');
|
|
25
|
+
|
|
26
|
+
// Determine OS and CPU for package.json
|
|
27
|
+
const OS_MAP = {
|
|
28
|
+
'macos-14': 'darwin',
|
|
29
|
+
'macos-13': 'darwin',
|
|
30
|
+
'ubuntu-22.04': 'linux',
|
|
31
|
+
'windows-2022': 'win32'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const osName = OS_MAP[osRunner] || process.platform;
|
|
35
|
+
|
|
36
|
+
console.log(`\n=== Creating platform package: @lloyal-labs/lloyal.node-${packageName} ===\n`);
|
|
37
|
+
|
|
38
|
+
// Create directories
|
|
39
|
+
fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
40
|
+
|
|
41
|
+
// Copy binaries
|
|
42
|
+
console.log('Copying binaries...');
|
|
43
|
+
|
|
44
|
+
// N-API binary
|
|
45
|
+
const nodeBinary = path.join(BUILD_DIR, 'lloyal.node');
|
|
46
|
+
if (!fs.existsSync(nodeBinary)) {
|
|
47
|
+
console.error(`❌ Error: lloyal.node not found at ${nodeBinary}`);
|
|
48
|
+
console.error('Available files in build/Release:');
|
|
49
|
+
if (fs.existsSync(BUILD_DIR)) {
|
|
50
|
+
fs.readdirSync(BUILD_DIR).forEach(f => console.error(` - ${f}`));
|
|
51
|
+
} else {
|
|
52
|
+
console.error(' (build/Release directory does not exist)');
|
|
53
|
+
}
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fs.copyFileSync(nodeBinary, path.join(BIN_DIR, 'lloyal.node'));
|
|
58
|
+
console.log(` ✓ Copied lloyal.node`);
|
|
59
|
+
|
|
60
|
+
// Shared libraries (platform-specific)
|
|
61
|
+
if (osName === 'darwin') {
|
|
62
|
+
const dylib = path.join(BUILD_DIR, 'libllama.dylib');
|
|
63
|
+
if (fs.existsSync(dylib)) {
|
|
64
|
+
fs.copyFileSync(dylib, path.join(BIN_DIR, 'libllama.dylib'));
|
|
65
|
+
console.log(` ✓ Copied libllama.dylib`);
|
|
66
|
+
} else {
|
|
67
|
+
console.warn(` ⚠️ libllama.dylib not found (optional)`);
|
|
68
|
+
}
|
|
69
|
+
} else if (osName === 'linux') {
|
|
70
|
+
const so = path.join(BUILD_DIR, 'libllama.so');
|
|
71
|
+
if (fs.existsSync(so)) {
|
|
72
|
+
fs.copyFileSync(so, path.join(BIN_DIR, 'libllama.so'));
|
|
73
|
+
console.log(` ✓ Copied libllama.so`);
|
|
74
|
+
} else {
|
|
75
|
+
console.warn(` ⚠️ libllama.so not found (optional)`);
|
|
76
|
+
}
|
|
77
|
+
} else if (osName === 'win32') {
|
|
78
|
+
// Copy all DLLs
|
|
79
|
+
const dlls = fs.readdirSync(BUILD_DIR).filter(f => f.endsWith('.dll'));
|
|
80
|
+
if (dlls.length > 0) {
|
|
81
|
+
dlls.forEach(dll => {
|
|
82
|
+
fs.copyFileSync(
|
|
83
|
+
path.join(BUILD_DIR, dll),
|
|
84
|
+
path.join(BIN_DIR, dll)
|
|
85
|
+
);
|
|
86
|
+
console.log(` ✓ Copied ${dll}`);
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
console.warn(` ⚠️ No DLLs found in build/Release (optional)`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Create package.json from template
|
|
94
|
+
console.log('\nGenerating package.json...');
|
|
95
|
+
const mainPackageJson = require(path.join(ROOT, 'package.json'));
|
|
96
|
+
const templatePath = path.join(ROOT, 'packages', 'template', 'package.json');
|
|
97
|
+
|
|
98
|
+
let pkgJson;
|
|
99
|
+
if (fs.existsSync(templatePath)) {
|
|
100
|
+
pkgJson = require(templatePath);
|
|
101
|
+
} else {
|
|
102
|
+
// Fallback template if file doesn't exist yet
|
|
103
|
+
pkgJson = {
|
|
104
|
+
name: '@lloyal-labs/lloyal.node-PLATFORM',
|
|
105
|
+
version: '0.0.0',
|
|
106
|
+
description: 'Lloyal native binary for PLATFORM',
|
|
107
|
+
main: 'index.js',
|
|
108
|
+
files: ['bin/', 'index.js'],
|
|
109
|
+
repository: {
|
|
110
|
+
type: 'git',
|
|
111
|
+
url: 'git+https://github.com/lloyal-ai/lloyal.node.git'
|
|
112
|
+
},
|
|
113
|
+
license: 'Apache-2.0'
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Update with actual values
|
|
118
|
+
pkgJson.name = `@lloyal-labs/lloyal.node-${packageName}`;
|
|
119
|
+
pkgJson.version = mainPackageJson.version;
|
|
120
|
+
pkgJson.description = `Lloyal native binary for ${packageName}`;
|
|
121
|
+
pkgJson.os = [osName];
|
|
122
|
+
pkgJson.cpu = [arch];
|
|
123
|
+
|
|
124
|
+
fs.writeFileSync(
|
|
125
|
+
path.join(PKG_DIR, 'package.json'),
|
|
126
|
+
JSON.stringify(pkgJson, null, 2) + '\n'
|
|
127
|
+
);
|
|
128
|
+
console.log(` ✓ Created package.json`);
|
|
129
|
+
|
|
130
|
+
// Create index.js
|
|
131
|
+
console.log('\nGenerating index.js...');
|
|
132
|
+
const indexJs = `// Platform-specific binary package for ${packageName}
|
|
133
|
+
// This file resolves to the native binary in bin/
|
|
134
|
+
|
|
135
|
+
const path = require('path');
|
|
136
|
+
|
|
137
|
+
module.exports = path.join(__dirname, 'bin', 'lloyal.node');
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
fs.writeFileSync(path.join(PKG_DIR, 'index.js'), indexJs);
|
|
141
|
+
console.log(` ✓ Created index.js`);
|
|
142
|
+
|
|
143
|
+
// Summary
|
|
144
|
+
console.log(`\n✅ Platform package created successfully!`);
|
|
145
|
+
console.log(`\nPackage: @lloyal-labs/lloyal.node-${packageName}@${pkgJson.version}`);
|
|
146
|
+
console.log(`Location: ${PKG_DIR}`);
|
|
147
|
+
console.log(`\nContents:`);
|
|
148
|
+
fs.readdirSync(BIN_DIR).forEach(f => {
|
|
149
|
+
const stats = fs.statSync(path.join(BIN_DIR, f));
|
|
150
|
+
const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
|
|
151
|
+
console.log(` - bin/${f} (${sizeMB} MB)`);
|
|
152
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
echo "Downloading test models..."
|
|
5
|
+
|
|
6
|
+
mkdir -p models
|
|
7
|
+
cd models
|
|
8
|
+
|
|
9
|
+
# SmolLM2 1.7B Instruct Q4_K_M (1.0GB)
|
|
10
|
+
if [ ! -f "SmolLM2-1.7B-Instruct-Q4_K_M.gguf" ]; then
|
|
11
|
+
echo " → Downloading SmolLM2-1.7B-Instruct-Q4_K_M.gguf..."
|
|
12
|
+
curl -L -o "SmolLM2-1.7B-Instruct-Q4_K_M.gguf" \
|
|
13
|
+
"https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct-GGUF/resolve/main/smollm2-1.7b-instruct-q4_k_m.gguf"
|
|
14
|
+
echo " ✓ Downloaded SmolLM2"
|
|
15
|
+
else
|
|
16
|
+
echo " ✓ SmolLM2 already exists"
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Nomic Embed Text v1.5 Q4_K_M (80MB)
|
|
20
|
+
if [ ! -f "nomic-embed-text-v1.5.Q4_K_M.gguf" ]; then
|
|
21
|
+
echo " → Downloading nomic-embed-text-v1.5.Q4_K_M.gguf..."
|
|
22
|
+
curl -L -o "nomic-embed-text-v1.5.Q4_K_M.gguf" \
|
|
23
|
+
"https://huggingface.co/nomic-ai/nomic-embed-text-v1.5-GGUF/resolve/main/nomic-embed-text-v1.5.Q4_K_M.gguf"
|
|
24
|
+
echo " ✓ Downloaded nomic-embed-text"
|
|
25
|
+
else
|
|
26
|
+
echo " ✓ nomic-embed-text already exists"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
echo ""
|
|
30
|
+
echo "✅ All test models ready"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Initialize git submodules during npm install
|
|
4
|
+
*
|
|
5
|
+
* This is necessary because npm doesn't automatically initialize submodules
|
|
6
|
+
* when installing from GitHub URLs. Without this, llama.cpp/ and liblloyal/
|
|
7
|
+
* won't exist, causing build failures.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const ROOT = path.join(__dirname, '..');
|
|
15
|
+
|
|
16
|
+
console.log('[init-submodules] Checking for git submodules...');
|
|
17
|
+
|
|
18
|
+
// Check if we're in a git repository
|
|
19
|
+
const isGitRepo = fs.existsSync(path.join(ROOT, '.git'));
|
|
20
|
+
|
|
21
|
+
if (!isGitRepo) {
|
|
22
|
+
console.log('[init-submodules] Not a git repository, skipping submodule initialization');
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check if submodules are already initialized
|
|
27
|
+
const llamaCppExists = fs.existsSync(path.join(ROOT, 'llama.cpp/.git'));
|
|
28
|
+
const libloyalExists = fs.existsSync(path.join(ROOT, 'liblloyal/.git'));
|
|
29
|
+
|
|
30
|
+
if (llamaCppExists && libloyalExists) {
|
|
31
|
+
console.log('[init-submodules] ✓ Submodules already initialized');
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Initialize submodules
|
|
36
|
+
console.log('[init-submodules] Initializing git submodules...');
|
|
37
|
+
try {
|
|
38
|
+
execSync('git submodule update --init --recursive', {
|
|
39
|
+
cwd: ROOT,
|
|
40
|
+
stdio: 'inherit'
|
|
41
|
+
});
|
|
42
|
+
console.log('[init-submodules] ✓ Submodules initialized successfully');
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('[init-submodules] Failed to initialize submodules:', error.message);
|
|
45
|
+
console.error('[init-submodules] Please run manually: git submodule update --init --recursive');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Smart installer for lloyal.node
|
|
4
|
+
*
|
|
5
|
+
* Strategy:
|
|
6
|
+
* 1. Check if prebuilt binary exists for this platform
|
|
7
|
+
* 2. If yes, copy to build/Release/ and exit
|
|
8
|
+
* 3. If no, show helpful error with build-from-source instructions
|
|
9
|
+
*
|
|
10
|
+
* Respects LLOYAL_GPU environment variable for GPU variant selection
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const PLATFORM = process.platform;
|
|
17
|
+
const ARCH = process.arch;
|
|
18
|
+
const ROOT = __dirname + '/..';
|
|
19
|
+
const BUILD_DIR = path.join(ROOT, 'build', 'Release');
|
|
20
|
+
|
|
21
|
+
// Logging helpers
|
|
22
|
+
const log = (msg) => console.log(`[lloyal.node] ${msg}`);
|
|
23
|
+
const error = (msg) => console.error(`[lloyal.node] ❌ ${msg}`);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if a platform package is installed and has binaries
|
|
27
|
+
*/
|
|
28
|
+
function findPrebuilt(packageName) {
|
|
29
|
+
try {
|
|
30
|
+
const pkgPath = require.resolve(packageName);
|
|
31
|
+
const binPath = require(packageName); // index.js exports path to binary
|
|
32
|
+
|
|
33
|
+
if (fs.existsSync(binPath)) {
|
|
34
|
+
const binDir = path.dirname(binPath);
|
|
35
|
+
return binDir;
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {
|
|
38
|
+
// Package not installed or doesn't export binary path
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Copy prebuilt binaries to build/Release/
|
|
45
|
+
*/
|
|
46
|
+
function installPrebuilt(binDir, packageName) {
|
|
47
|
+
log(`Found prebuilt binaries in ${packageName}`);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
// Create build/Release directory
|
|
51
|
+
fs.mkdirSync(BUILD_DIR, { recursive: true });
|
|
52
|
+
|
|
53
|
+
// Copy all files from bin directory
|
|
54
|
+
const files = fs.readdirSync(binDir);
|
|
55
|
+
files.forEach(file => {
|
|
56
|
+
const src = path.join(binDir, file);
|
|
57
|
+
const dest = path.join(BUILD_DIR, file);
|
|
58
|
+
|
|
59
|
+
if (fs.statSync(src).isFile()) {
|
|
60
|
+
fs.copyFileSync(src, dest);
|
|
61
|
+
log(` ✓ Copied ${file}`);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
log(`✅ Installed prebuilt binaries successfully`);
|
|
66
|
+
process.exit(0);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
error(`Failed to install prebuilt: ${e.message}`);
|
|
69
|
+
// Don't exit - fall through to source build
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Main installation logic
|
|
75
|
+
*/
|
|
76
|
+
function main() {
|
|
77
|
+
log(`Platform: ${PLATFORM}-${ARCH}`);
|
|
78
|
+
|
|
79
|
+
// 1. Check for user-specified GPU variant via environment variable
|
|
80
|
+
if (process.env.LLOYAL_GPU) {
|
|
81
|
+
const gpu = process.env.LLOYAL_GPU.toLowerCase();
|
|
82
|
+
const packageName = `@lloyal-labs/lloyal.node-${PLATFORM}-${ARCH}-${gpu}`;
|
|
83
|
+
|
|
84
|
+
log(`LLOYAL_GPU=${gpu}, looking for ${packageName}...`);
|
|
85
|
+
const binDir = findPrebuilt(packageName);
|
|
86
|
+
|
|
87
|
+
if (binDir) {
|
|
88
|
+
installPrebuilt(binDir, packageName);
|
|
89
|
+
return; // exit(0) called in installPrebuilt
|
|
90
|
+
} else {
|
|
91
|
+
log(` ⚠️ Package ${packageName} not found`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 2. Check for GPU variants in priority order
|
|
96
|
+
const gpuVariants = ['cuda', 'vulkan'];
|
|
97
|
+
for (const gpu of gpuVariants) {
|
|
98
|
+
const packageName = `@lloyal-labs/lloyal.node-${PLATFORM}-${ARCH}-${gpu}`;
|
|
99
|
+
const binDir = findPrebuilt(packageName);
|
|
100
|
+
|
|
101
|
+
if (binDir) {
|
|
102
|
+
log(`Auto-detected GPU variant: ${gpu}`);
|
|
103
|
+
installPrebuilt(binDir, packageName);
|
|
104
|
+
return; // exit(0) called in installPrebuilt
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 3. Check for default platform package (CPU or Metal on macOS)
|
|
109
|
+
const defaultPackage = `@lloyal-labs/lloyal.node-${PLATFORM}-${ARCH}`;
|
|
110
|
+
const binDir = findPrebuilt(defaultPackage);
|
|
111
|
+
|
|
112
|
+
if (binDir) {
|
|
113
|
+
installPrebuilt(binDir, defaultPackage);
|
|
114
|
+
return; // exit(0) called in installPrebuilt
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 4. No prebuilt found - error with helpful message
|
|
118
|
+
log('');
|
|
119
|
+
error('No prebuilt binary found for your platform');
|
|
120
|
+
log('');
|
|
121
|
+
log(` Platform: ${PLATFORM}-${ARCH}`);
|
|
122
|
+
log('');
|
|
123
|
+
log(' Options:');
|
|
124
|
+
log(' 1. Install a platform-specific package:');
|
|
125
|
+
log(` npm install @lloyal-labs/lloyal.node-${PLATFORM}-${ARCH}`);
|
|
126
|
+
log('');
|
|
127
|
+
log(' 2. Build from source (requires C++20, CMake 3.18+):');
|
|
128
|
+
log(' git clone --recursive https://github.com/lloyal-ai/lloyal.node.git');
|
|
129
|
+
log(' cd lloyal.node && npm run build');
|
|
130
|
+
log('');
|
|
131
|
+
log(' See: https://github.com/lloyal-ai/lloyal.node#building');
|
|
132
|
+
log('');
|
|
133
|
+
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Run installer
|
|
138
|
+
main();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Synchronize all platform package versions with main package version
|
|
4
|
+
*
|
|
5
|
+
* Ensures optionalDependencies in package.json all reference the current version
|
|
6
|
+
* Run before publishing or after `npm version`
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const ROOT = path.join(__dirname, '..');
|
|
13
|
+
const PKG_JSON_PATH = path.join(ROOT, 'package.json');
|
|
14
|
+
|
|
15
|
+
console.log('[sync-versions] Synchronizing package versions...\n');
|
|
16
|
+
|
|
17
|
+
// Read main package.json
|
|
18
|
+
const pkg = JSON.parse(fs.readFileSync(PKG_JSON_PATH, 'utf8'));
|
|
19
|
+
const version = pkg.version;
|
|
20
|
+
|
|
21
|
+
console.log(`Main package version: ${version}`);
|
|
22
|
+
|
|
23
|
+
// Update optionalDependencies
|
|
24
|
+
if (pkg.optionalDependencies) {
|
|
25
|
+
console.log('\nUpdating optionalDependencies:');
|
|
26
|
+
|
|
27
|
+
Object.keys(pkg.optionalDependencies).forEach(dep => {
|
|
28
|
+
const oldVersion = pkg.optionalDependencies[dep];
|
|
29
|
+
pkg.optionalDependencies[dep] = version;
|
|
30
|
+
|
|
31
|
+
if (oldVersion !== version) {
|
|
32
|
+
console.log(` ${dep}: ${oldVersion} → ${version}`);
|
|
33
|
+
} else {
|
|
34
|
+
console.log(` ${dep}: ${version} (unchanged)`);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Write updated package.json
|
|
39
|
+
fs.writeFileSync(PKG_JSON_PATH, JSON.stringify(pkg, null, 2) + '\n');
|
|
40
|
+
console.log('\n✅ package.json updated');
|
|
41
|
+
} else {
|
|
42
|
+
console.log('\n⚠️ No optionalDependencies found in package.json');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Update any existing platform packages in packages/ directory
|
|
46
|
+
const PACKAGES_DIR = path.join(ROOT, 'packages');
|
|
47
|
+
|
|
48
|
+
if (fs.existsSync(PACKAGES_DIR)) {
|
|
49
|
+
const dirs = fs.readdirSync(PACKAGES_DIR).filter(f => {
|
|
50
|
+
const stat = fs.statSync(path.join(PACKAGES_DIR, f));
|
|
51
|
+
return stat.isDirectory() && f !== 'template';
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (dirs.length > 0) {
|
|
55
|
+
console.log('\nUpdating platform packages:');
|
|
56
|
+
|
|
57
|
+
dirs.forEach(dir => {
|
|
58
|
+
const pkgPath = path.join(PACKAGES_DIR, dir, 'package.json');
|
|
59
|
+
|
|
60
|
+
if (fs.existsSync(pkgPath)) {
|
|
61
|
+
const platformPkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
62
|
+
const oldVersion = platformPkg.version;
|
|
63
|
+
|
|
64
|
+
platformPkg.version = version;
|
|
65
|
+
fs.writeFileSync(pkgPath, JSON.stringify(platformPkg, null, 2) + '\n');
|
|
66
|
+
|
|
67
|
+
if (oldVersion !== version) {
|
|
68
|
+
console.log(` ${platformPkg.name}: ${oldVersion} → ${version}`);
|
|
69
|
+
} else {
|
|
70
|
+
console.log(` ${platformPkg.name}: ${version} (unchanged)`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
console.log('\n✅ Platform packages updated');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log('\n✅ Version synchronization complete!');
|