wllama-service 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/README.md +56 -0
- package/dist/index.d.mts +58 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +175 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +144 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +39 -0
- package/scripts/postinstall.js +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# wllama-service
|
|
2
|
+
|
|
3
|
+
A framework-agnostic browser LLM service wrapper built on [@wllama/wllama](https://github.com/ngxson/wllama).
|
|
4
|
+
Runs GGUF models locally in the browser with WebGPU acceleration and WebAssembly fallback.
|
|
5
|
+
|
|
6
|
+
## Requirements
|
|
7
|
+
|
|
8
|
+
Your web server must send these headers for multi-threading and WebGPU to work:
|
|
9
|
+
```
|
|
10
|
+
Cross-Origin-Opener-Policy: same-origin
|
|
11
|
+
Cross-Origin-Embedder-Policy: require-corp
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Copy `wllama.wasm` from `node_modules/@wllama/wllama/esm/wasm/wllama.wasm` to your public folder
|
|
15
|
+
and pass its URL via `wasmPath`.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install wllama-service @wllama/wllama
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { WllamaService } from 'wllama-service';
|
|
27
|
+
|
|
28
|
+
const service = new WllamaService({
|
|
29
|
+
wasmPath: '/wllama/wllama.wasm', // path in your public folder
|
|
30
|
+
nCtx: 2048,
|
|
31
|
+
nGpuLayers: 999, // set 0 to disable WebGPU
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Check browser support
|
|
35
|
+
const env = service.checkEnvironment();
|
|
36
|
+
console.log('WebGPU available:', env.hasWebGPU);
|
|
37
|
+
|
|
38
|
+
// Load a GGUF file from file input
|
|
39
|
+
const result = await service.loadModel(file, (progress) => {
|
|
40
|
+
console.log(`Loading: ${progress}%`);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Generate
|
|
44
|
+
const response = await service.generate({
|
|
45
|
+
system: 'You are a helpful assistant.',
|
|
46
|
+
prompt: 'Hello!',
|
|
47
|
+
maxTokens: 256,
|
|
48
|
+
temperature: 0.7,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log(response.text);
|
|
52
|
+
console.log(`Generated in ${response.timeMs}ms`);
|
|
53
|
+
|
|
54
|
+
// Unload when done
|
|
55
|
+
await service.unload();
|
|
56
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
interface WllamaServiceConfig {
|
|
2
|
+
/** Public URL where wllama.wasm is served from. Default: '/wllama/wllama.wasm' */
|
|
3
|
+
wasmPath?: string;
|
|
4
|
+
/** Number of GPU layers to offload. Default: 999 (all). Set 0 to disable WebGPU. */
|
|
5
|
+
nGpuLayers?: number;
|
|
6
|
+
/** Context window size. Default: 2048 */
|
|
7
|
+
nCtx?: number;
|
|
8
|
+
}
|
|
9
|
+
interface GenerateRequest {
|
|
10
|
+
prompt: string;
|
|
11
|
+
system?: string;
|
|
12
|
+
maxTokens?: number;
|
|
13
|
+
temperature?: number;
|
|
14
|
+
topK?: number;
|
|
15
|
+
topP?: number;
|
|
16
|
+
}
|
|
17
|
+
interface GenerateResult {
|
|
18
|
+
success: boolean;
|
|
19
|
+
text?: string;
|
|
20
|
+
error?: string;
|
|
21
|
+
timeMs?: number;
|
|
22
|
+
}
|
|
23
|
+
interface LoadModelResult {
|
|
24
|
+
success: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
usedWebGPU?: boolean;
|
|
27
|
+
}
|
|
28
|
+
interface ConnectionResult {
|
|
29
|
+
success: boolean;
|
|
30
|
+
error?: string;
|
|
31
|
+
hasWebGPU?: boolean;
|
|
32
|
+
crossOriginIsolated?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare class WllamaService {
|
|
36
|
+
private wllama;
|
|
37
|
+
private modelName;
|
|
38
|
+
private config;
|
|
39
|
+
constructor(config?: WllamaServiceConfig);
|
|
40
|
+
/** Check browser compatibility and capabilities */
|
|
41
|
+
checkEnvironment(): ConnectionResult;
|
|
42
|
+
/** Load a GGUF model file from the user's filesystem */
|
|
43
|
+
loadModel(file: File, onProgress?: (progress: number) => void): Promise<LoadModelResult>;
|
|
44
|
+
/** Generate a response from the loaded model */
|
|
45
|
+
generate(req: GenerateRequest): Promise<GenerateResult>;
|
|
46
|
+
/** Unload the current model and free memory */
|
|
47
|
+
unload(): Promise<void>;
|
|
48
|
+
/** Whether a model is currently loaded */
|
|
49
|
+
get isLoaded(): boolean;
|
|
50
|
+
/** Name of the currently loaded model */
|
|
51
|
+
get currentModel(): string;
|
|
52
|
+
private storeInCache;
|
|
53
|
+
private deleteFromCache;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
declare function copyWasmToPublic(publicDir: string): void;
|
|
57
|
+
|
|
58
|
+
export { type ConnectionResult, type GenerateRequest, type GenerateResult, type LoadModelResult, WllamaService, type WllamaServiceConfig, copyWasmToPublic };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
interface WllamaServiceConfig {
|
|
2
|
+
/** Public URL where wllama.wasm is served from. Default: '/wllama/wllama.wasm' */
|
|
3
|
+
wasmPath?: string;
|
|
4
|
+
/** Number of GPU layers to offload. Default: 999 (all). Set 0 to disable WebGPU. */
|
|
5
|
+
nGpuLayers?: number;
|
|
6
|
+
/** Context window size. Default: 2048 */
|
|
7
|
+
nCtx?: number;
|
|
8
|
+
}
|
|
9
|
+
interface GenerateRequest {
|
|
10
|
+
prompt: string;
|
|
11
|
+
system?: string;
|
|
12
|
+
maxTokens?: number;
|
|
13
|
+
temperature?: number;
|
|
14
|
+
topK?: number;
|
|
15
|
+
topP?: number;
|
|
16
|
+
}
|
|
17
|
+
interface GenerateResult {
|
|
18
|
+
success: boolean;
|
|
19
|
+
text?: string;
|
|
20
|
+
error?: string;
|
|
21
|
+
timeMs?: number;
|
|
22
|
+
}
|
|
23
|
+
interface LoadModelResult {
|
|
24
|
+
success: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
usedWebGPU?: boolean;
|
|
27
|
+
}
|
|
28
|
+
interface ConnectionResult {
|
|
29
|
+
success: boolean;
|
|
30
|
+
error?: string;
|
|
31
|
+
hasWebGPU?: boolean;
|
|
32
|
+
crossOriginIsolated?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare class WllamaService {
|
|
36
|
+
private wllama;
|
|
37
|
+
private modelName;
|
|
38
|
+
private config;
|
|
39
|
+
constructor(config?: WllamaServiceConfig);
|
|
40
|
+
/** Check browser compatibility and capabilities */
|
|
41
|
+
checkEnvironment(): ConnectionResult;
|
|
42
|
+
/** Load a GGUF model file from the user's filesystem */
|
|
43
|
+
loadModel(file: File, onProgress?: (progress: number) => void): Promise<LoadModelResult>;
|
|
44
|
+
/** Generate a response from the loaded model */
|
|
45
|
+
generate(req: GenerateRequest): Promise<GenerateResult>;
|
|
46
|
+
/** Unload the current model and free memory */
|
|
47
|
+
unload(): Promise<void>;
|
|
48
|
+
/** Whether a model is currently loaded */
|
|
49
|
+
get isLoaded(): boolean;
|
|
50
|
+
/** Name of the currently loaded model */
|
|
51
|
+
get currentModel(): string;
|
|
52
|
+
private storeInCache;
|
|
53
|
+
private deleteFromCache;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
declare function copyWasmToPublic(publicDir: string): void;
|
|
57
|
+
|
|
58
|
+
export { type ConnectionResult, type GenerateRequest, type GenerateResult, type LoadModelResult, WllamaService, type WllamaServiceConfig, copyWasmToPublic };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
WllamaService: () => WllamaService,
|
|
34
|
+
copyWasmToPublic: () => copyWasmToPublic
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/WllamaService.ts
|
|
39
|
+
var import_esm = require("@wllama/wllama/esm/index.js");
|
|
40
|
+
var WllamaService = class {
|
|
41
|
+
constructor(config = {}) {
|
|
42
|
+
this.wllama = null;
|
|
43
|
+
this.modelName = "";
|
|
44
|
+
this.config = {
|
|
45
|
+
wasmPath: config.wasmPath ?? "/wllama/wllama.wasm",
|
|
46
|
+
nGpuLayers: config.nGpuLayers ?? 999,
|
|
47
|
+
nCtx: config.nCtx ?? 2048
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Check browser compatibility and capabilities */
|
|
51
|
+
checkEnvironment() {
|
|
52
|
+
if (typeof WebAssembly === "undefined") {
|
|
53
|
+
return { success: false, error: "WebAssembly is not supported in this browser." };
|
|
54
|
+
}
|
|
55
|
+
if (!("caches" in window)) {
|
|
56
|
+
return { success: false, error: "Cache API is not supported in this browser." };
|
|
57
|
+
}
|
|
58
|
+
const hasWebGPU = !!navigator.gpu;
|
|
59
|
+
const isolated = window.crossOriginIsolated;
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
hasWebGPU,
|
|
63
|
+
crossOriginIsolated: isolated
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Load a GGUF model file from the user's filesystem */
|
|
67
|
+
async loadModel(file, onProgress) {
|
|
68
|
+
const env = this.checkEnvironment();
|
|
69
|
+
if (!env.success) return { success: false, error: env.error };
|
|
70
|
+
await this.unload();
|
|
71
|
+
try {
|
|
72
|
+
onProgress?.(10);
|
|
73
|
+
const configPaths = { default: this.config.wasmPath };
|
|
74
|
+
this.wllama = new import_esm.Wllama(configPaths);
|
|
75
|
+
onProgress?.(30);
|
|
76
|
+
await this.storeInCache(file);
|
|
77
|
+
onProgress?.(50);
|
|
78
|
+
const hasWebGPU = !!navigator.gpu;
|
|
79
|
+
await this.wllama.loadModel([file], {
|
|
80
|
+
n_ctx: this.config.nCtx,
|
|
81
|
+
...hasWebGPU && this.config.nGpuLayers > 0 ? { n_gpu_layers: this.config.nGpuLayers } : {}
|
|
82
|
+
});
|
|
83
|
+
this.modelName = file.name;
|
|
84
|
+
onProgress?.(100);
|
|
85
|
+
return { success: true, usedWebGPU: hasWebGPU };
|
|
86
|
+
} catch (e) {
|
|
87
|
+
await this.unload();
|
|
88
|
+
return { success: false, error: e.message };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Generate a response from the loaded model */
|
|
92
|
+
async generate(req) {
|
|
93
|
+
if (!this.wllama) {
|
|
94
|
+
return { success: false, error: "No model loaded. Call loadModel() first." };
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const t0 = performance.now();
|
|
98
|
+
const messages = [];
|
|
99
|
+
if (req.system) messages.push({ role: "system", content: req.system });
|
|
100
|
+
messages.push({ role: "user", content: req.prompt });
|
|
101
|
+
const response = await this.wllama.createChatCompletion({
|
|
102
|
+
messages,
|
|
103
|
+
max_tokens: req.maxTokens ?? 512,
|
|
104
|
+
temperature: req.temperature ?? 0.7,
|
|
105
|
+
top_k: req.topK ?? 40,
|
|
106
|
+
top_p: req.topP ?? 0.95
|
|
107
|
+
});
|
|
108
|
+
const text = response?.choices?.[0]?.message?.content ?? "";
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
text,
|
|
112
|
+
timeMs: Math.round(performance.now() - t0)
|
|
113
|
+
};
|
|
114
|
+
} catch (e) {
|
|
115
|
+
return { success: false, error: e.message || "Generation failed" };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** Unload the current model and free memory */
|
|
119
|
+
async unload() {
|
|
120
|
+
if (this.wllama) {
|
|
121
|
+
try {
|
|
122
|
+
await this.wllama.exit();
|
|
123
|
+
} catch (_) {
|
|
124
|
+
}
|
|
125
|
+
this.wllama = null;
|
|
126
|
+
}
|
|
127
|
+
if (this.modelName) {
|
|
128
|
+
try {
|
|
129
|
+
await this.deleteFromCache(this.modelName);
|
|
130
|
+
} catch (_) {
|
|
131
|
+
}
|
|
132
|
+
this.modelName = "";
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/** Whether a model is currently loaded */
|
|
136
|
+
get isLoaded() {
|
|
137
|
+
return this.wllama !== null;
|
|
138
|
+
}
|
|
139
|
+
/** Name of the currently loaded model */
|
|
140
|
+
get currentModel() {
|
|
141
|
+
return this.modelName;
|
|
142
|
+
}
|
|
143
|
+
async storeInCache(file) {
|
|
144
|
+
const url = `${window.location.origin}/wllama-local-models/${file.name}`;
|
|
145
|
+
const cache = await caches.open("wllama-local-models");
|
|
146
|
+
await cache.put(url, new Response(file, {
|
|
147
|
+
headers: { "Content-Type": "application/octet-stream" }
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
async deleteFromCache(fileName) {
|
|
151
|
+
const url = `${window.location.origin}/wllama-local-models/${fileName}`;
|
|
152
|
+
const cache = await caches.open("wllama-local-models");
|
|
153
|
+
await cache.delete(url);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// src/copy-wasm.ts
|
|
158
|
+
var import_fs = __toESM(require("fs"));
|
|
159
|
+
var import_path = __toESM(require("path"));
|
|
160
|
+
function copyWasmToPublic(publicDir) {
|
|
161
|
+
const src = import_path.default.resolve(
|
|
162
|
+
require.resolve("@wllama/wllama/esm/index.js"),
|
|
163
|
+
"../../wasm/wllama.wasm"
|
|
164
|
+
);
|
|
165
|
+
const dest = import_path.default.join(publicDir, "wllama", "wllama.wasm");
|
|
166
|
+
import_fs.default.mkdirSync(import_path.default.dirname(dest), { recursive: true });
|
|
167
|
+
import_fs.default.copyFileSync(src, dest);
|
|
168
|
+
console.log(`\u2713 Copied wllama.wasm to ${dest}`);
|
|
169
|
+
}
|
|
170
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
171
|
+
0 && (module.exports = {
|
|
172
|
+
WllamaService,
|
|
173
|
+
copyWasmToPublic
|
|
174
|
+
});
|
|
175
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/WllamaService.ts","../src/copy-wasm.ts"],"sourcesContent":["export { WllamaService } from './WllamaService';\nexport type {\n WllamaServiceConfig,\n GenerateRequest,\n GenerateResult,\n LoadModelResult,\n ConnectionResult,\n} from './types';\nexport { copyWasmToPublic } from './copy-wasm';","import { Wllama } from '@wllama/wllama/esm/index.js';\nimport type {\n WllamaServiceConfig,\n GenerateRequest,\n GenerateResult,\n LoadModelResult,\n ConnectionResult,\n} from './types';\n\nexport class WllamaService {\n private wllama: Wllama | null = null;\n private modelName: string = '';\n private config: Required<WllamaServiceConfig>;\n\n constructor(config: WllamaServiceConfig = {}) {\n this.config = {\n wasmPath: config.wasmPath ?? '/wllama/wllama.wasm',\n nGpuLayers: config.nGpuLayers ?? 999,\n nCtx: config.nCtx ?? 2048,\n };\n }\n\n /** Check browser compatibility and capabilities */\n checkEnvironment(): ConnectionResult {\n if (typeof WebAssembly === 'undefined') {\n return { success: false, error: 'WebAssembly is not supported in this browser.' };\n }\n if (!('caches' in window)) {\n return { success: false, error: 'Cache API is not supported in this browser.' };\n }\n const hasWebGPU = !!(navigator as any).gpu;\n const isolated = window.crossOriginIsolated;\n return {\n success: true,\n hasWebGPU,\n crossOriginIsolated: isolated,\n };\n }\n\n /** Load a GGUF model file from the user's filesystem */\n async loadModel(\n file: File,\n onProgress?: (progress: number) => void,\n ): Promise<LoadModelResult> {\n const env = this.checkEnvironment();\n if (!env.success) return { success: false, error: env.error };\n\n await this.unload();\n\n try {\n onProgress?.(10);\n\n const configPaths = { default: this.config.wasmPath };\n this.wllama = new Wllama(configPaths as any);\n onProgress?.(30);\n\n await this.storeInCache(file);\n onProgress?.(50);\n\n const hasWebGPU = !!(navigator as any).gpu;\n await (this.wllama as any).loadModel([file], {\n n_ctx: this.config.nCtx,\n ...(hasWebGPU && this.config.nGpuLayers > 0\n ? { n_gpu_layers: this.config.nGpuLayers }\n : {}),\n });\n\n this.modelName = file.name;\n onProgress?.(100);\n\n return { success: true, usedWebGPU: hasWebGPU };\n } catch (e: any) {\n await this.unload();\n return { success: false, error: e.message };\n }\n }\n\n /** Generate a response from the loaded model */\n async generate(req: GenerateRequest): Promise<GenerateResult> {\n if (!this.wllama) {\n return { success: false, error: 'No model loaded. Call loadModel() first.' };\n }\n\n try {\n const t0 = performance.now();\n\n const messages: { role: string; content: string }[] = [];\n if (req.system) messages.push({ role: 'system', content: req.system });\n messages.push({ role: 'user', content: req.prompt });\n\n const response = await (this.wllama as any).createChatCompletion({\n messages,\n max_tokens: req.maxTokens ?? 512,\n temperature: req.temperature ?? 0.7,\n top_k: req.topK ?? 40,\n top_p: req.topP ?? 0.95,\n });\n\n const text: string = response?.choices?.[0]?.message?.content ?? '';\n return {\n success: true,\n text,\n timeMs: Math.round(performance.now() - t0),\n };\n } catch (e: any) {\n return { success: false, error: e.message || 'Generation failed' };\n }\n }\n\n /** Unload the current model and free memory */\n async unload(): Promise<void> {\n if (this.wllama) {\n try { await (this.wllama as any).exit(); } catch (_) {}\n this.wllama = null;\n }\n if (this.modelName) {\n try { await this.deleteFromCache(this.modelName); } catch (_) {}\n this.modelName = '';\n }\n }\n\n /** Whether a model is currently loaded */\n get isLoaded(): boolean {\n return this.wllama !== null;\n }\n\n /** Name of the currently loaded model */\n get currentModel(): string {\n return this.modelName;\n }\n\n private async storeInCache(file: File): Promise<void> {\n const url = `${window.location.origin}/wllama-local-models/${file.name}`;\n const cache = await caches.open('wllama-local-models');\n await cache.put(url, new Response(file, {\n headers: { 'Content-Type': 'application/octet-stream' },\n }));\n }\n\n private async deleteFromCache(fileName: string): Promise<void> {\n const url = `${window.location.origin}/wllama-local-models/${fileName}`;\n const cache = await caches.open('wllama-local-models');\n await cache.delete(url);\n }\n}","import fs from 'fs';\nimport path from 'path';\n\nexport function copyWasmToPublic(publicDir: string): void {\n const src = path.resolve(\n require.resolve('@wllama/wllama/esm/index.js'),\n '../../wasm/wllama.wasm'\n );\n const dest = path.join(publicDir, 'wllama', 'wllama.wasm');\n \n fs.mkdirSync(path.dirname(dest), { recursive: true });\n fs.copyFileSync(src, dest);\n console.log(`✓ Copied wllama.wasm to ${dest}`);\n}"],"mappings":"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","names":["path","fs"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/WllamaService.ts
|
|
9
|
+
import { Wllama } from "@wllama/wllama/esm/index.js";
|
|
10
|
+
var WllamaService = class {
|
|
11
|
+
constructor(config = {}) {
|
|
12
|
+
this.wllama = null;
|
|
13
|
+
this.modelName = "";
|
|
14
|
+
this.config = {
|
|
15
|
+
wasmPath: config.wasmPath ?? "/wllama/wllama.wasm",
|
|
16
|
+
nGpuLayers: config.nGpuLayers ?? 999,
|
|
17
|
+
nCtx: config.nCtx ?? 2048
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Check browser compatibility and capabilities */
|
|
21
|
+
checkEnvironment() {
|
|
22
|
+
if (typeof WebAssembly === "undefined") {
|
|
23
|
+
return { success: false, error: "WebAssembly is not supported in this browser." };
|
|
24
|
+
}
|
|
25
|
+
if (!("caches" in window)) {
|
|
26
|
+
return { success: false, error: "Cache API is not supported in this browser." };
|
|
27
|
+
}
|
|
28
|
+
const hasWebGPU = !!navigator.gpu;
|
|
29
|
+
const isolated = window.crossOriginIsolated;
|
|
30
|
+
return {
|
|
31
|
+
success: true,
|
|
32
|
+
hasWebGPU,
|
|
33
|
+
crossOriginIsolated: isolated
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Load a GGUF model file from the user's filesystem */
|
|
37
|
+
async loadModel(file, onProgress) {
|
|
38
|
+
const env = this.checkEnvironment();
|
|
39
|
+
if (!env.success) return { success: false, error: env.error };
|
|
40
|
+
await this.unload();
|
|
41
|
+
try {
|
|
42
|
+
onProgress?.(10);
|
|
43
|
+
const configPaths = { default: this.config.wasmPath };
|
|
44
|
+
this.wllama = new Wllama(configPaths);
|
|
45
|
+
onProgress?.(30);
|
|
46
|
+
await this.storeInCache(file);
|
|
47
|
+
onProgress?.(50);
|
|
48
|
+
const hasWebGPU = !!navigator.gpu;
|
|
49
|
+
await this.wllama.loadModel([file], {
|
|
50
|
+
n_ctx: this.config.nCtx,
|
|
51
|
+
...hasWebGPU && this.config.nGpuLayers > 0 ? { n_gpu_layers: this.config.nGpuLayers } : {}
|
|
52
|
+
});
|
|
53
|
+
this.modelName = file.name;
|
|
54
|
+
onProgress?.(100);
|
|
55
|
+
return { success: true, usedWebGPU: hasWebGPU };
|
|
56
|
+
} catch (e) {
|
|
57
|
+
await this.unload();
|
|
58
|
+
return { success: false, error: e.message };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** Generate a response from the loaded model */
|
|
62
|
+
async generate(req) {
|
|
63
|
+
if (!this.wllama) {
|
|
64
|
+
return { success: false, error: "No model loaded. Call loadModel() first." };
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const t0 = performance.now();
|
|
68
|
+
const messages = [];
|
|
69
|
+
if (req.system) messages.push({ role: "system", content: req.system });
|
|
70
|
+
messages.push({ role: "user", content: req.prompt });
|
|
71
|
+
const response = await this.wllama.createChatCompletion({
|
|
72
|
+
messages,
|
|
73
|
+
max_tokens: req.maxTokens ?? 512,
|
|
74
|
+
temperature: req.temperature ?? 0.7,
|
|
75
|
+
top_k: req.topK ?? 40,
|
|
76
|
+
top_p: req.topP ?? 0.95
|
|
77
|
+
});
|
|
78
|
+
const text = response?.choices?.[0]?.message?.content ?? "";
|
|
79
|
+
return {
|
|
80
|
+
success: true,
|
|
81
|
+
text,
|
|
82
|
+
timeMs: Math.round(performance.now() - t0)
|
|
83
|
+
};
|
|
84
|
+
} catch (e) {
|
|
85
|
+
return { success: false, error: e.message || "Generation failed" };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Unload the current model and free memory */
|
|
89
|
+
async unload() {
|
|
90
|
+
if (this.wllama) {
|
|
91
|
+
try {
|
|
92
|
+
await this.wllama.exit();
|
|
93
|
+
} catch (_) {
|
|
94
|
+
}
|
|
95
|
+
this.wllama = null;
|
|
96
|
+
}
|
|
97
|
+
if (this.modelName) {
|
|
98
|
+
try {
|
|
99
|
+
await this.deleteFromCache(this.modelName);
|
|
100
|
+
} catch (_) {
|
|
101
|
+
}
|
|
102
|
+
this.modelName = "";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Whether a model is currently loaded */
|
|
106
|
+
get isLoaded() {
|
|
107
|
+
return this.wllama !== null;
|
|
108
|
+
}
|
|
109
|
+
/** Name of the currently loaded model */
|
|
110
|
+
get currentModel() {
|
|
111
|
+
return this.modelName;
|
|
112
|
+
}
|
|
113
|
+
async storeInCache(file) {
|
|
114
|
+
const url = `${window.location.origin}/wllama-local-models/${file.name}`;
|
|
115
|
+
const cache = await caches.open("wllama-local-models");
|
|
116
|
+
await cache.put(url, new Response(file, {
|
|
117
|
+
headers: { "Content-Type": "application/octet-stream" }
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
async deleteFromCache(fileName) {
|
|
121
|
+
const url = `${window.location.origin}/wllama-local-models/${fileName}`;
|
|
122
|
+
const cache = await caches.open("wllama-local-models");
|
|
123
|
+
await cache.delete(url);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/copy-wasm.ts
|
|
128
|
+
import fs from "fs";
|
|
129
|
+
import path from "path";
|
|
130
|
+
function copyWasmToPublic(publicDir) {
|
|
131
|
+
const src = path.resolve(
|
|
132
|
+
__require.resolve("@wllama/wllama/esm/index.js"),
|
|
133
|
+
"../../wasm/wllama.wasm"
|
|
134
|
+
);
|
|
135
|
+
const dest = path.join(publicDir, "wllama", "wllama.wasm");
|
|
136
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
137
|
+
fs.copyFileSync(src, dest);
|
|
138
|
+
console.log(`\u2713 Copied wllama.wasm to ${dest}`);
|
|
139
|
+
}
|
|
140
|
+
export {
|
|
141
|
+
WllamaService,
|
|
142
|
+
copyWasmToPublic
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/WllamaService.ts","../src/copy-wasm.ts"],"sourcesContent":["import { Wllama } from '@wllama/wllama/esm/index.js';\nimport type {\n WllamaServiceConfig,\n GenerateRequest,\n GenerateResult,\n LoadModelResult,\n ConnectionResult,\n} from './types';\n\nexport class WllamaService {\n private wllama: Wllama | null = null;\n private modelName: string = '';\n private config: Required<WllamaServiceConfig>;\n\n constructor(config: WllamaServiceConfig = {}) {\n this.config = {\n wasmPath: config.wasmPath ?? '/wllama/wllama.wasm',\n nGpuLayers: config.nGpuLayers ?? 999,\n nCtx: config.nCtx ?? 2048,\n };\n }\n\n /** Check browser compatibility and capabilities */\n checkEnvironment(): ConnectionResult {\n if (typeof WebAssembly === 'undefined') {\n return { success: false, error: 'WebAssembly is not supported in this browser.' };\n }\n if (!('caches' in window)) {\n return { success: false, error: 'Cache API is not supported in this browser.' };\n }\n const hasWebGPU = !!(navigator as any).gpu;\n const isolated = window.crossOriginIsolated;\n return {\n success: true,\n hasWebGPU,\n crossOriginIsolated: isolated,\n };\n }\n\n /** Load a GGUF model file from the user's filesystem */\n async loadModel(\n file: File,\n onProgress?: (progress: number) => void,\n ): Promise<LoadModelResult> {\n const env = this.checkEnvironment();\n if (!env.success) return { success: false, error: env.error };\n\n await this.unload();\n\n try {\n onProgress?.(10);\n\n const configPaths = { default: this.config.wasmPath };\n this.wllama = new Wllama(configPaths as any);\n onProgress?.(30);\n\n await this.storeInCache(file);\n onProgress?.(50);\n\n const hasWebGPU = !!(navigator as any).gpu;\n await (this.wllama as any).loadModel([file], {\n n_ctx: this.config.nCtx,\n ...(hasWebGPU && this.config.nGpuLayers > 0\n ? { n_gpu_layers: this.config.nGpuLayers }\n : {}),\n });\n\n this.modelName = file.name;\n onProgress?.(100);\n\n return { success: true, usedWebGPU: hasWebGPU };\n } catch (e: any) {\n await this.unload();\n return { success: false, error: e.message };\n }\n }\n\n /** Generate a response from the loaded model */\n async generate(req: GenerateRequest): Promise<GenerateResult> {\n if (!this.wllama) {\n return { success: false, error: 'No model loaded. Call loadModel() first.' };\n }\n\n try {\n const t0 = performance.now();\n\n const messages: { role: string; content: string }[] = [];\n if (req.system) messages.push({ role: 'system', content: req.system });\n messages.push({ role: 'user', content: req.prompt });\n\n const response = await (this.wllama as any).createChatCompletion({\n messages,\n max_tokens: req.maxTokens ?? 512,\n temperature: req.temperature ?? 0.7,\n top_k: req.topK ?? 40,\n top_p: req.topP ?? 0.95,\n });\n\n const text: string = response?.choices?.[0]?.message?.content ?? '';\n return {\n success: true,\n text,\n timeMs: Math.round(performance.now() - t0),\n };\n } catch (e: any) {\n return { success: false, error: e.message || 'Generation failed' };\n }\n }\n\n /** Unload the current model and free memory */\n async unload(): Promise<void> {\n if (this.wllama) {\n try { await (this.wllama as any).exit(); } catch (_) {}\n this.wllama = null;\n }\n if (this.modelName) {\n try { await this.deleteFromCache(this.modelName); } catch (_) {}\n this.modelName = '';\n }\n }\n\n /** Whether a model is currently loaded */\n get isLoaded(): boolean {\n return this.wllama !== null;\n }\n\n /** Name of the currently loaded model */\n get currentModel(): string {\n return this.modelName;\n }\n\n private async storeInCache(file: File): Promise<void> {\n const url = `${window.location.origin}/wllama-local-models/${file.name}`;\n const cache = await caches.open('wllama-local-models');\n await cache.put(url, new Response(file, {\n headers: { 'Content-Type': 'application/octet-stream' },\n }));\n }\n\n private async deleteFromCache(fileName: string): Promise<void> {\n const url = `${window.location.origin}/wllama-local-models/${fileName}`;\n const cache = await caches.open('wllama-local-models');\n await cache.delete(url);\n }\n}","import fs from 'fs';\nimport path from 'path';\n\nexport function copyWasmToPublic(publicDir: string): void {\n const src = path.resolve(\n require.resolve('@wllama/wllama/esm/index.js'),\n '../../wasm/wllama.wasm'\n );\n const dest = path.join(publicDir, 'wllama', 'wllama.wasm');\n \n fs.mkdirSync(path.dirname(dest), { recursive: true });\n fs.copyFileSync(src, dest);\n console.log(`✓ Copied wllama.wasm to ${dest}`);\n}"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wllama-service",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Framework-agnostic browser LLM service wrapper with WebGPU and WebAssembly support",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"scripts",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"prepublishOnly": "npm run build",
|
|
24
|
+
"postinstall": "node scripts/postinstall.js"
|
|
25
|
+
},
|
|
26
|
+
"bin": {
|
|
27
|
+
"wllama-copy-wasm": "./scripts/postinstall.js"
|
|
28
|
+
},
|
|
29
|
+
"keywords": ["wllama", "llm", "webgpu", "webassembly", "llama", "browser-ai", "gguf"],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@wllama/wllama": ">=3.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@wllama/wllama": "^3.0.0",
|
|
36
|
+
"tsup": "^8.0.0",
|
|
37
|
+
"typescript": "^5.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
// Find the consuming app's public directory
|
|
6
|
+
const possiblePublicDirs = [
|
|
7
|
+
path.resolve(process.cwd(), '../../public'), // monorepo
|
|
8
|
+
path.resolve(process.cwd(), '../../../public'), // deeper monorepo
|
|
9
|
+
path.resolve(process.cwd(), 'public'), // root install
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const wasmSrc = path.resolve(
|
|
13
|
+
__dirname,
|
|
14
|
+
'../node_modules/@wllama/wllama/esm/wasm/wllama.wasm'
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(wasmSrc)) {
|
|
18
|
+
console.warn('[wllama-service] Could not find wllama.wasm — run copy manually');
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const publicDir = possiblePublicDirs.find(fs.existsSync);
|
|
23
|
+
if (!publicDir) {
|
|
24
|
+
console.warn('[wllama-service] Could not find public/ directory — copy wllama.wasm manually');
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const dest = path.join(publicDir, 'wllama', 'wllama.wasm');
|
|
29
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
30
|
+
fs.copyFileSync(wasmSrc, dest);
|
|
31
|
+
console.log(`[wllama-service] ✓ Copied wllama.wasm to ${dest}`);
|