wllama-service 2.0.4 → 2.0.6
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/dist/index.d.mts +27 -9
- package/dist/index.d.ts +27 -9
- package/dist/index.js +132 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +132 -59
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -13,6 +13,8 @@ interface GenerateRequest {
|
|
|
13
13
|
temperature?: number;
|
|
14
14
|
topK?: number;
|
|
15
15
|
topP?: number;
|
|
16
|
+
stop?: string | string[];
|
|
17
|
+
abortSignal?: AbortSignal;
|
|
16
18
|
}
|
|
17
19
|
interface GenerateResult {
|
|
18
20
|
success: boolean;
|
|
@@ -35,22 +37,38 @@ interface ConnectionResult {
|
|
|
35
37
|
declare class WllamaService {
|
|
36
38
|
private wllama;
|
|
37
39
|
private modelName;
|
|
38
|
-
private config;
|
|
40
|
+
private readonly config;
|
|
39
41
|
constructor(config?: WllamaServiceConfig);
|
|
40
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Check WebGPU without directly accessing navigator.gpu.
|
|
44
|
+
*
|
|
45
|
+
* This avoids requiring @webgpu/types merely for feature detection.
|
|
46
|
+
*/
|
|
47
|
+
private hasWebGPU;
|
|
48
|
+
/** Check browser compatibility and capabilities. */
|
|
41
49
|
checkEnvironment(): ConnectionResult;
|
|
42
|
-
/** Load a GGUF model file from the user's filesystem */
|
|
50
|
+
/** Load a GGUF model file from the user's filesystem. */
|
|
43
51
|
loadModel(file: File, onProgress?: (progress: number) => void): Promise<LoadModelResult>;
|
|
44
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Generate a chat response using the model's embedded chat template.
|
|
54
|
+
*
|
|
55
|
+
* Suitable for instruction-tuned models such as Phi-3 Instruct,
|
|
56
|
+
* Llama Instruct, ChatML models, and similar chat models.
|
|
57
|
+
*/
|
|
45
58
|
generate(req: GenerateRequest): Promise<GenerateResult>;
|
|
46
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Generate a raw completion without applying a chat template.
|
|
61
|
+
*
|
|
62
|
+
* Suitable for base completion models, manually formatted prompts,
|
|
63
|
+
* few-shot completion, fill-in-the-middle, and custom templates.
|
|
64
|
+
*/
|
|
65
|
+
generateCompletion(req: GenerateRequest): Promise<GenerateResult>;
|
|
66
|
+
/** Unload the current model and release its resources. */
|
|
47
67
|
unload(): Promise<void>;
|
|
48
|
-
/** Whether a model is currently loaded */
|
|
68
|
+
/** Whether a model is currently loaded. */
|
|
49
69
|
get isLoaded(): boolean;
|
|
50
|
-
/** Name of the currently loaded model */
|
|
70
|
+
/** Name of the currently loaded model. */
|
|
51
71
|
get currentModel(): string;
|
|
52
|
-
private storeInCache;
|
|
53
|
-
private deleteFromCache;
|
|
54
72
|
}
|
|
55
73
|
|
|
56
74
|
export { type ConnectionResult, type GenerateRequest, type GenerateResult, type LoadModelResult, WllamaService, type WllamaServiceConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ interface GenerateRequest {
|
|
|
13
13
|
temperature?: number;
|
|
14
14
|
topK?: number;
|
|
15
15
|
topP?: number;
|
|
16
|
+
stop?: string | string[];
|
|
17
|
+
abortSignal?: AbortSignal;
|
|
16
18
|
}
|
|
17
19
|
interface GenerateResult {
|
|
18
20
|
success: boolean;
|
|
@@ -35,22 +37,38 @@ interface ConnectionResult {
|
|
|
35
37
|
declare class WllamaService {
|
|
36
38
|
private wllama;
|
|
37
39
|
private modelName;
|
|
38
|
-
private config;
|
|
40
|
+
private readonly config;
|
|
39
41
|
constructor(config?: WllamaServiceConfig);
|
|
40
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Check WebGPU without directly accessing navigator.gpu.
|
|
44
|
+
*
|
|
45
|
+
* This avoids requiring @webgpu/types merely for feature detection.
|
|
46
|
+
*/
|
|
47
|
+
private hasWebGPU;
|
|
48
|
+
/** Check browser compatibility and capabilities. */
|
|
41
49
|
checkEnvironment(): ConnectionResult;
|
|
42
|
-
/** Load a GGUF model file from the user's filesystem */
|
|
50
|
+
/** Load a GGUF model file from the user's filesystem. */
|
|
43
51
|
loadModel(file: File, onProgress?: (progress: number) => void): Promise<LoadModelResult>;
|
|
44
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Generate a chat response using the model's embedded chat template.
|
|
54
|
+
*
|
|
55
|
+
* Suitable for instruction-tuned models such as Phi-3 Instruct,
|
|
56
|
+
* Llama Instruct, ChatML models, and similar chat models.
|
|
57
|
+
*/
|
|
45
58
|
generate(req: GenerateRequest): Promise<GenerateResult>;
|
|
46
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Generate a raw completion without applying a chat template.
|
|
61
|
+
*
|
|
62
|
+
* Suitable for base completion models, manually formatted prompts,
|
|
63
|
+
* few-shot completion, fill-in-the-middle, and custom templates.
|
|
64
|
+
*/
|
|
65
|
+
generateCompletion(req: GenerateRequest): Promise<GenerateResult>;
|
|
66
|
+
/** Unload the current model and release its resources. */
|
|
47
67
|
unload(): Promise<void>;
|
|
48
|
-
/** Whether a model is currently loaded */
|
|
68
|
+
/** Whether a model is currently loaded. */
|
|
49
69
|
get isLoaded(): boolean;
|
|
50
|
-
/** Name of the currently loaded model */
|
|
70
|
+
/** Name of the currently loaded model. */
|
|
51
71
|
get currentModel(): string;
|
|
52
|
-
private storeInCache;
|
|
53
|
-
private deleteFromCache;
|
|
54
72
|
}
|
|
55
73
|
|
|
56
74
|
export { type ConnectionResult, type GenerateRequest, type GenerateResult, type LoadModelResult, WllamaService, type WllamaServiceConfig };
|
package/dist/index.js
CHANGED
|
@@ -36,116 +36,189 @@ var WllamaService = class {
|
|
|
36
36
|
nCtx: config.nCtx ?? 2048
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Check WebGPU without directly accessing navigator.gpu.
|
|
41
|
+
*
|
|
42
|
+
* This avoids requiring @webgpu/types merely for feature detection.
|
|
43
|
+
*/
|
|
44
|
+
hasWebGPU() {
|
|
45
|
+
return typeof navigator !== "undefined" && "gpu" in navigator;
|
|
46
|
+
}
|
|
47
|
+
/** Check browser compatibility and capabilities. */
|
|
40
48
|
checkEnvironment() {
|
|
41
|
-
if (typeof
|
|
42
|
-
return {
|
|
49
|
+
if (typeof window === "undefined" || typeof navigator === "undefined") {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: "WllamaService can only run in a browser environment."
|
|
53
|
+
};
|
|
43
54
|
}
|
|
44
|
-
if (
|
|
45
|
-
return {
|
|
55
|
+
if (typeof WebAssembly === "undefined") {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
error: "WebAssembly is not supported in this browser."
|
|
59
|
+
};
|
|
46
60
|
}
|
|
47
|
-
const hasWebGPU = !!navigator.gpu;
|
|
48
|
-
const isolated = window.crossOriginIsolated;
|
|
49
61
|
return {
|
|
50
62
|
success: true,
|
|
51
|
-
hasWebGPU,
|
|
52
|
-
crossOriginIsolated:
|
|
63
|
+
hasWebGPU: this.hasWebGPU(),
|
|
64
|
+
crossOriginIsolated: window.crossOriginIsolated
|
|
53
65
|
};
|
|
54
66
|
}
|
|
55
|
-
/** Load a GGUF model file from the user's filesystem */
|
|
67
|
+
/** Load a GGUF model file from the user's filesystem. */
|
|
56
68
|
async loadModel(file, onProgress) {
|
|
57
|
-
const
|
|
58
|
-
if (!
|
|
69
|
+
const environment = this.checkEnvironment();
|
|
70
|
+
if (!environment.success) {
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
error: environment.error
|
|
74
|
+
};
|
|
75
|
+
}
|
|
59
76
|
await this.unload();
|
|
60
77
|
try {
|
|
61
78
|
onProgress?.(10);
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
this.wllama = new import_esm.Wllama({
|
|
80
|
+
default: this.config.wasmPath
|
|
81
|
+
});
|
|
64
82
|
onProgress?.(30);
|
|
65
|
-
|
|
66
|
-
onProgress?.(50);
|
|
67
|
-
const hasWebGPU = !!navigator.gpu;
|
|
83
|
+
const useWebGPU = this.hasWebGPU() && this.config.nGpuLayers > 0;
|
|
68
84
|
await this.wllama.loadModel([file], {
|
|
69
85
|
n_ctx: this.config.nCtx,
|
|
70
|
-
|
|
86
|
+
n_gpu_layers: useWebGPU ? this.config.nGpuLayers : 0,
|
|
87
|
+
jinja: true
|
|
71
88
|
});
|
|
72
89
|
this.modelName = file.name;
|
|
73
90
|
onProgress?.(100);
|
|
74
|
-
return {
|
|
75
|
-
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
usedWebGPU: useWebGPU
|
|
94
|
+
};
|
|
95
|
+
} catch (error) {
|
|
76
96
|
await this.unload();
|
|
77
|
-
|
|
78
|
-
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: error instanceof Error ? error.message : "Failed to load model"
|
|
100
|
+
};
|
|
79
101
|
}
|
|
80
102
|
}
|
|
81
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Generate a chat response using the model's embedded chat template.
|
|
105
|
+
*
|
|
106
|
+
* Suitable for instruction-tuned models such as Phi-3 Instruct,
|
|
107
|
+
* Llama Instruct, ChatML models, and similar chat models.
|
|
108
|
+
*/
|
|
82
109
|
async generate(req) {
|
|
83
110
|
if (!this.wllama) {
|
|
84
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
success: false,
|
|
113
|
+
error: "No model loaded. Call loadModel() first."
|
|
114
|
+
};
|
|
85
115
|
}
|
|
86
116
|
try {
|
|
87
|
-
const
|
|
117
|
+
const startedAt = performance.now();
|
|
88
118
|
const messages = [];
|
|
89
|
-
if (req.system)
|
|
90
|
-
|
|
119
|
+
if (req.system) {
|
|
120
|
+
messages.push({
|
|
121
|
+
role: "system",
|
|
122
|
+
content: req.system
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
messages.push({
|
|
126
|
+
role: "user",
|
|
127
|
+
content: req.prompt
|
|
128
|
+
});
|
|
91
129
|
const response = await this.wllama.createChatCompletion({
|
|
92
130
|
messages,
|
|
131
|
+
stream: false,
|
|
93
132
|
max_tokens: req.maxTokens ?? 512,
|
|
94
133
|
temperature: req.temperature ?? 0.7,
|
|
95
134
|
top_k: req.topK ?? 40,
|
|
96
|
-
top_p: req.topP ?? 0.95
|
|
135
|
+
top_p: req.topP ?? 0.95,
|
|
136
|
+
stop: req.stop,
|
|
137
|
+
abortSignal: req.abortSignal
|
|
97
138
|
});
|
|
98
|
-
const text = response
|
|
139
|
+
const text = response.choices?.[0]?.message?.content ?? "";
|
|
99
140
|
return {
|
|
100
141
|
success: true,
|
|
101
142
|
text,
|
|
102
|
-
timeMs: Math.round(
|
|
143
|
+
timeMs: Math.round(
|
|
144
|
+
performance.now() - startedAt
|
|
145
|
+
)
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
error: error instanceof Error ? error.message : "Generation failed"
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Generate a raw completion without applying a chat template.
|
|
156
|
+
*
|
|
157
|
+
* Suitable for base completion models, manually formatted prompts,
|
|
158
|
+
* few-shot completion, fill-in-the-middle, and custom templates.
|
|
159
|
+
*/
|
|
160
|
+
async generateCompletion(req) {
|
|
161
|
+
if (!this.wllama) {
|
|
162
|
+
return {
|
|
163
|
+
success: false,
|
|
164
|
+
error: "No model loaded. Call loadModel() first."
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const startedAt = performance.now();
|
|
169
|
+
const prompt = req.system ? `${req.system}
|
|
170
|
+
|
|
171
|
+
${req.prompt}` : req.prompt;
|
|
172
|
+
const response = await this.wllama.createCompletion({
|
|
173
|
+
prompt,
|
|
174
|
+
stream: false,
|
|
175
|
+
max_tokens: req.maxTokens ?? 512,
|
|
176
|
+
temperature: req.temperature ?? 0.7,
|
|
177
|
+
top_k: req.topK ?? 40,
|
|
178
|
+
top_p: req.topP ?? 0.95,
|
|
179
|
+
stop: req.stop,
|
|
180
|
+
abortSignal: req.abortSignal
|
|
181
|
+
});
|
|
182
|
+
const choice = response.choices?.[0];
|
|
183
|
+
console.debug("[wllama] raw completion", {
|
|
184
|
+
text: choice?.text,
|
|
185
|
+
finishReason: choice?.finish_reason,
|
|
186
|
+
usage: response.usage,
|
|
187
|
+
timings: response.timings
|
|
188
|
+
});
|
|
189
|
+
return {
|
|
190
|
+
success: true,
|
|
191
|
+
text: choice?.text ?? "",
|
|
192
|
+
timeMs: Math.round(
|
|
193
|
+
performance.now() - startedAt
|
|
194
|
+
)
|
|
195
|
+
};
|
|
196
|
+
} catch (error) {
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
error: error instanceof Error ? error.message : "Generation failed"
|
|
103
200
|
};
|
|
104
|
-
} catch (e) {
|
|
105
|
-
const message = e instanceof Error ? e.message : "Generation failed";
|
|
106
|
-
return { success: false, error: message };
|
|
107
201
|
}
|
|
108
202
|
}
|
|
109
|
-
/** Unload the current model and
|
|
203
|
+
/** Unload the current model and release its resources. */
|
|
110
204
|
async unload() {
|
|
111
205
|
if (this.wllama) {
|
|
112
206
|
try {
|
|
113
207
|
await this.wllama.exit();
|
|
114
|
-
} catch
|
|
208
|
+
} catch {
|
|
115
209
|
}
|
|
116
210
|
this.wllama = null;
|
|
117
211
|
}
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
await this.deleteFromCache(this.modelName);
|
|
121
|
-
} catch (_) {
|
|
122
|
-
}
|
|
123
|
-
this.modelName = "";
|
|
124
|
-
}
|
|
212
|
+
this.modelName = "";
|
|
125
213
|
}
|
|
126
|
-
/** Whether a model is currently loaded */
|
|
214
|
+
/** Whether a model is currently loaded. */
|
|
127
215
|
get isLoaded() {
|
|
128
216
|
return this.wllama !== null;
|
|
129
217
|
}
|
|
130
|
-
/** Name of the currently loaded model */
|
|
218
|
+
/** Name of the currently loaded model. */
|
|
131
219
|
get currentModel() {
|
|
132
220
|
return this.modelName;
|
|
133
221
|
}
|
|
134
|
-
async storeInCache(file) {
|
|
135
|
-
const url = `${window.location.origin}/wllama-local-models/${file.name}`;
|
|
136
|
-
const cache = await caches.open("wllama-local-models");
|
|
137
|
-
await cache.put(
|
|
138
|
-
url,
|
|
139
|
-
new Response(file, {
|
|
140
|
-
headers: { "Content-Type": "application/octet-stream" }
|
|
141
|
-
})
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
async deleteFromCache(fileName) {
|
|
145
|
-
const url = `${window.location.origin}/wllama-local-models/${fileName}`;
|
|
146
|
-
const cache = await caches.open("wllama-local-models");
|
|
147
|
-
await cache.delete(url);
|
|
148
|
-
}
|
|
149
222
|
};
|
|
150
223
|
// Annotate the CommonJS export names for ESM import in node:
|
|
151
224
|
0 && (module.exports = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/WllamaService.ts"],"sourcesContent":["export { WllamaService } from './WllamaService';\nexport type {\n WllamaServiceConfig,\n GenerateRequest,\n GenerateResult,\n LoadModelResult,\n ConnectionResult,\n} from './types';","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 // AssetsPathConfig requires `default` — this is the key the\n // Wllama runtime actually reads (confirmed against the real\n // @wllama/wllama@3.5.1 implementation, not just its docs).\n // No `as any` needed: this shape is type-correct as written.\n const configPaths = { default: this.config.wasmPath };\n this.wllama = new Wllama(configPaths);\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: unknown) {\n await this.unload();\n const message = e instanceof Error ? e.message : 'Failed to load model';\n return { success: false, error: 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: unknown) {\n const message = e instanceof Error ? e.message : 'Generation failed';\n return { success: false, error: message };\n }\n }\n\n /** Unload the current model and free memory */\n async unload(): Promise<void> {\n if (this.wllama) {\n try {\n await (this.wllama as any).exit();\n } catch (_) {\n /* ignore */\n }\n this.wllama = null;\n }\n if (this.modelName) {\n try {\n await this.deleteFromCache(this.modelName);\n } catch (_) {\n /* ignore */\n }\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(\n url,\n new Response(file, {\n headers: { 'Content-Type': 'application/octet-stream' },\n }),\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}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/WllamaService.ts"],"sourcesContent":["export { WllamaService } from './WllamaService';\nexport type {\n WllamaServiceConfig,\n GenerateRequest,\n GenerateResult,\n LoadModelResult,\n ConnectionResult,\n} from './types';","// src/WllamaService.ts\n\nimport { Wllama } from \"@wllama/wllama/esm/index.js\";\n\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 = \"\";\n private readonly 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 /**\n * Check WebGPU without directly accessing navigator.gpu.\n *\n * This avoids requiring @webgpu/types merely for feature detection.\n */\n private hasWebGPU(): boolean {\n return typeof navigator !== \"undefined\" && \"gpu\" in navigator;\n }\n\n /** Check browser compatibility and capabilities. */\n checkEnvironment(): ConnectionResult {\n if (\n typeof window === \"undefined\" ||\n typeof navigator === \"undefined\"\n ) {\n return {\n success: false,\n error: \"WllamaService can only run in a browser environment.\",\n };\n }\n\n if (typeof WebAssembly === \"undefined\") {\n return {\n success: false,\n error: \"WebAssembly is not supported in this browser.\",\n };\n }\n\n return {\n success: true,\n hasWebGPU: this.hasWebGPU(),\n crossOriginIsolated: window.crossOriginIsolated,\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 environment = this.checkEnvironment();\n\n if (!environment.success) {\n return {\n success: false,\n error: environment.error,\n };\n }\n\n await this.unload();\n\n try {\n onProgress?.(10);\n\n this.wllama = new Wllama({\n default: this.config.wasmPath,\n });\n\n onProgress?.(30);\n\n const useWebGPU =\n this.hasWebGPU() && this.config.nGpuLayers > 0;\n\n await this.wllama.loadModel([file], {\n n_ctx: this.config.nCtx,\n n_gpu_layers: useWebGPU\n ? this.config.nGpuLayers\n : 0,\n jinja: true,\n });\n\n this.modelName = file.name;\n\n onProgress?.(100);\n\n return {\n success: true,\n usedWebGPU: useWebGPU,\n };\n } catch (error: unknown) {\n await this.unload();\n\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Failed to load model\",\n };\n }\n }\n\n /**\n * Generate a chat response using the model's embedded chat template.\n *\n * Suitable for instruction-tuned models such as Phi-3 Instruct,\n * Llama Instruct, ChatML models, and similar chat models.\n */\n async generate(\n req: GenerateRequest,\n ): Promise<GenerateResult> {\n if (!this.wllama) {\n return {\n success: false,\n error: \"No model loaded. Call loadModel() first.\",\n };\n }\n\n try {\n const startedAt = performance.now();\n\n const messages: Array<{\n role: \"system\" | \"user\";\n content: string;\n }> = [];\n\n if (req.system) {\n messages.push({\n role: \"system\",\n content: req.system,\n });\n }\n\n messages.push({\n role: \"user\",\n content: req.prompt,\n });\n\n /*\n * Keep the cast here if the installed @wllama/wllama declaration\n * does not expose the exact createChatCompletion signature.\n */\n const response = await (\n this.wllama as unknown as {\n createChatCompletion(options: {\n messages: Array<{\n role: \"system\" | \"user\";\n content: string;\n }>;\n stream: false;\n max_tokens: number;\n temperature: number;\n top_k: number;\n top_p: number;\n stop?: string | string[];\n abortSignal?: AbortSignal;\n }): Promise<{\n choices?: Array<{\n message?: {\n content?: string;\n };\n }>;\n }>;\n }\n ).createChatCompletion({\n messages,\n stream: false,\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 stop: req.stop,\n abortSignal: req.abortSignal,\n });\n\n const text =\n response.choices?.[0]?.message?.content ?? \"\";\n\n return {\n success: true,\n text,\n timeMs: Math.round(\n performance.now() - startedAt,\n ),\n };\n } catch (error: unknown) {\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Generation failed\",\n };\n }\n }\n\n /**\n * Generate a raw completion without applying a chat template.\n *\n * Suitable for base completion models, manually formatted prompts,\n * few-shot completion, fill-in-the-middle, and custom templates.\n */\n async generateCompletion(\n req: GenerateRequest,\n ): Promise<GenerateResult> {\n if (!this.wllama) {\n return {\n success: false,\n error: \"No model loaded. Call loadModel() first.\",\n };\n }\n\n try {\n const startedAt = performance.now();\n\n const prompt = req.system\n ? `${req.system}\\n\\n${req.prompt}`\n : req.prompt;\n\n const response =\n await this.wllama.createCompletion({\n prompt,\n stream: false,\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 stop: req.stop,\n abortSignal: req.abortSignal,\n });\n\n const choice = response.choices?.[0];\n\n console.debug(\"[wllama] raw completion\", {\n text: choice?.text,\n finishReason: choice?.finish_reason,\n usage: response.usage,\n timings: response.timings,\n });\n\n return {\n success: true,\n text: choice?.text ?? \"\",\n timeMs: Math.round(\n performance.now() - startedAt,\n ),\n };\n } catch (error: unknown) {\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Generation failed\",\n };\n }\n }\n\n /** Unload the current model and release its resources. */\n async unload(): Promise<void> {\n if (this.wllama) {\n try {\n await this.wllama.exit();\n } catch {\n // Ignore shutdown errors.\n }\n\n this.wllama = null;\n }\n\n this.modelName = \"\";\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}"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -10,116 +10,189 @@ var WllamaService = class {
|
|
|
10
10
|
nCtx: config.nCtx ?? 2048
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Check WebGPU without directly accessing navigator.gpu.
|
|
15
|
+
*
|
|
16
|
+
* This avoids requiring @webgpu/types merely for feature detection.
|
|
17
|
+
*/
|
|
18
|
+
hasWebGPU() {
|
|
19
|
+
return typeof navigator !== "undefined" && "gpu" in navigator;
|
|
20
|
+
}
|
|
21
|
+
/** Check browser compatibility and capabilities. */
|
|
14
22
|
checkEnvironment() {
|
|
15
|
-
if (typeof
|
|
16
|
-
return {
|
|
23
|
+
if (typeof window === "undefined" || typeof navigator === "undefined") {
|
|
24
|
+
return {
|
|
25
|
+
success: false,
|
|
26
|
+
error: "WllamaService can only run in a browser environment."
|
|
27
|
+
};
|
|
17
28
|
}
|
|
18
|
-
if (
|
|
19
|
-
return {
|
|
29
|
+
if (typeof WebAssembly === "undefined") {
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
error: "WebAssembly is not supported in this browser."
|
|
33
|
+
};
|
|
20
34
|
}
|
|
21
|
-
const hasWebGPU = !!navigator.gpu;
|
|
22
|
-
const isolated = window.crossOriginIsolated;
|
|
23
35
|
return {
|
|
24
36
|
success: true,
|
|
25
|
-
hasWebGPU,
|
|
26
|
-
crossOriginIsolated:
|
|
37
|
+
hasWebGPU: this.hasWebGPU(),
|
|
38
|
+
crossOriginIsolated: window.crossOriginIsolated
|
|
27
39
|
};
|
|
28
40
|
}
|
|
29
|
-
/** Load a GGUF model file from the user's filesystem */
|
|
41
|
+
/** Load a GGUF model file from the user's filesystem. */
|
|
30
42
|
async loadModel(file, onProgress) {
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
43
|
+
const environment = this.checkEnvironment();
|
|
44
|
+
if (!environment.success) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: environment.error
|
|
48
|
+
};
|
|
49
|
+
}
|
|
33
50
|
await this.unload();
|
|
34
51
|
try {
|
|
35
52
|
onProgress?.(10);
|
|
36
|
-
|
|
37
|
-
|
|
53
|
+
this.wllama = new Wllama({
|
|
54
|
+
default: this.config.wasmPath
|
|
55
|
+
});
|
|
38
56
|
onProgress?.(30);
|
|
39
|
-
|
|
40
|
-
onProgress?.(50);
|
|
41
|
-
const hasWebGPU = !!navigator.gpu;
|
|
57
|
+
const useWebGPU = this.hasWebGPU() && this.config.nGpuLayers > 0;
|
|
42
58
|
await this.wllama.loadModel([file], {
|
|
43
59
|
n_ctx: this.config.nCtx,
|
|
44
|
-
|
|
60
|
+
n_gpu_layers: useWebGPU ? this.config.nGpuLayers : 0,
|
|
61
|
+
jinja: true
|
|
45
62
|
});
|
|
46
63
|
this.modelName = file.name;
|
|
47
64
|
onProgress?.(100);
|
|
48
|
-
return {
|
|
49
|
-
|
|
65
|
+
return {
|
|
66
|
+
success: true,
|
|
67
|
+
usedWebGPU: useWebGPU
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
50
70
|
await this.unload();
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
error: error instanceof Error ? error.message : "Failed to load model"
|
|
74
|
+
};
|
|
53
75
|
}
|
|
54
76
|
}
|
|
55
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* Generate a chat response using the model's embedded chat template.
|
|
79
|
+
*
|
|
80
|
+
* Suitable for instruction-tuned models such as Phi-3 Instruct,
|
|
81
|
+
* Llama Instruct, ChatML models, and similar chat models.
|
|
82
|
+
*/
|
|
56
83
|
async generate(req) {
|
|
57
84
|
if (!this.wllama) {
|
|
58
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
error: "No model loaded. Call loadModel() first."
|
|
88
|
+
};
|
|
59
89
|
}
|
|
60
90
|
try {
|
|
61
|
-
const
|
|
91
|
+
const startedAt = performance.now();
|
|
62
92
|
const messages = [];
|
|
63
|
-
if (req.system)
|
|
64
|
-
|
|
93
|
+
if (req.system) {
|
|
94
|
+
messages.push({
|
|
95
|
+
role: "system",
|
|
96
|
+
content: req.system
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
messages.push({
|
|
100
|
+
role: "user",
|
|
101
|
+
content: req.prompt
|
|
102
|
+
});
|
|
65
103
|
const response = await this.wllama.createChatCompletion({
|
|
66
104
|
messages,
|
|
105
|
+
stream: false,
|
|
67
106
|
max_tokens: req.maxTokens ?? 512,
|
|
68
107
|
temperature: req.temperature ?? 0.7,
|
|
69
108
|
top_k: req.topK ?? 40,
|
|
70
|
-
top_p: req.topP ?? 0.95
|
|
109
|
+
top_p: req.topP ?? 0.95,
|
|
110
|
+
stop: req.stop,
|
|
111
|
+
abortSignal: req.abortSignal
|
|
71
112
|
});
|
|
72
|
-
const text = response
|
|
113
|
+
const text = response.choices?.[0]?.message?.content ?? "";
|
|
73
114
|
return {
|
|
74
115
|
success: true,
|
|
75
116
|
text,
|
|
76
|
-
timeMs: Math.round(
|
|
117
|
+
timeMs: Math.round(
|
|
118
|
+
performance.now() - startedAt
|
|
119
|
+
)
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
error: error instanceof Error ? error.message : "Generation failed"
|
|
77
125
|
};
|
|
78
|
-
} catch (e) {
|
|
79
|
-
const message = e instanceof Error ? e.message : "Generation failed";
|
|
80
|
-
return { success: false, error: message };
|
|
81
126
|
}
|
|
82
127
|
}
|
|
83
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* Generate a raw completion without applying a chat template.
|
|
130
|
+
*
|
|
131
|
+
* Suitable for base completion models, manually formatted prompts,
|
|
132
|
+
* few-shot completion, fill-in-the-middle, and custom templates.
|
|
133
|
+
*/
|
|
134
|
+
async generateCompletion(req) {
|
|
135
|
+
if (!this.wllama) {
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
error: "No model loaded. Call loadModel() first."
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const startedAt = performance.now();
|
|
143
|
+
const prompt = req.system ? `${req.system}
|
|
144
|
+
|
|
145
|
+
${req.prompt}` : req.prompt;
|
|
146
|
+
const response = await this.wllama.createCompletion({
|
|
147
|
+
prompt,
|
|
148
|
+
stream: false,
|
|
149
|
+
max_tokens: req.maxTokens ?? 512,
|
|
150
|
+
temperature: req.temperature ?? 0.7,
|
|
151
|
+
top_k: req.topK ?? 40,
|
|
152
|
+
top_p: req.topP ?? 0.95,
|
|
153
|
+
stop: req.stop,
|
|
154
|
+
abortSignal: req.abortSignal
|
|
155
|
+
});
|
|
156
|
+
const choice = response.choices?.[0];
|
|
157
|
+
console.debug("[wllama] raw completion", {
|
|
158
|
+
text: choice?.text,
|
|
159
|
+
finishReason: choice?.finish_reason,
|
|
160
|
+
usage: response.usage,
|
|
161
|
+
timings: response.timings
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
success: true,
|
|
165
|
+
text: choice?.text ?? "",
|
|
166
|
+
timeMs: Math.round(
|
|
167
|
+
performance.now() - startedAt
|
|
168
|
+
)
|
|
169
|
+
};
|
|
170
|
+
} catch (error) {
|
|
171
|
+
return {
|
|
172
|
+
success: false,
|
|
173
|
+
error: error instanceof Error ? error.message : "Generation failed"
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** Unload the current model and release its resources. */
|
|
84
178
|
async unload() {
|
|
85
179
|
if (this.wllama) {
|
|
86
180
|
try {
|
|
87
181
|
await this.wllama.exit();
|
|
88
|
-
} catch
|
|
182
|
+
} catch {
|
|
89
183
|
}
|
|
90
184
|
this.wllama = null;
|
|
91
185
|
}
|
|
92
|
-
|
|
93
|
-
try {
|
|
94
|
-
await this.deleteFromCache(this.modelName);
|
|
95
|
-
} catch (_) {
|
|
96
|
-
}
|
|
97
|
-
this.modelName = "";
|
|
98
|
-
}
|
|
186
|
+
this.modelName = "";
|
|
99
187
|
}
|
|
100
|
-
/** Whether a model is currently loaded */
|
|
188
|
+
/** Whether a model is currently loaded. */
|
|
101
189
|
get isLoaded() {
|
|
102
190
|
return this.wllama !== null;
|
|
103
191
|
}
|
|
104
|
-
/** Name of the currently loaded model */
|
|
192
|
+
/** Name of the currently loaded model. */
|
|
105
193
|
get currentModel() {
|
|
106
194
|
return this.modelName;
|
|
107
195
|
}
|
|
108
|
-
async storeInCache(file) {
|
|
109
|
-
const url = `${window.location.origin}/wllama-local-models/${file.name}`;
|
|
110
|
-
const cache = await caches.open("wllama-local-models");
|
|
111
|
-
await cache.put(
|
|
112
|
-
url,
|
|
113
|
-
new Response(file, {
|
|
114
|
-
headers: { "Content-Type": "application/octet-stream" }
|
|
115
|
-
})
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
async deleteFromCache(fileName) {
|
|
119
|
-
const url = `${window.location.origin}/wllama-local-models/${fileName}`;
|
|
120
|
-
const cache = await caches.open("wllama-local-models");
|
|
121
|
-
await cache.delete(url);
|
|
122
|
-
}
|
|
123
196
|
};
|
|
124
197
|
export {
|
|
125
198
|
WllamaService
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/WllamaService.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 // AssetsPathConfig requires `default` — this is the key the\n // Wllama runtime actually reads (confirmed against the real\n // @wllama/wllama@3.5.1 implementation, not just its docs).\n // No `as any` needed: this shape is type-correct as written.\n const configPaths = { default: this.config.wasmPath };\n this.wllama = new Wllama(configPaths);\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: unknown) {\n await this.unload();\n const message = e instanceof Error ? e.message : 'Failed to load model';\n return { success: false, error: 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: unknown) {\n const message = e instanceof Error ? e.message : 'Generation failed';\n return { success: false, error: message };\n }\n }\n\n /** Unload the current model and free memory */\n async unload(): Promise<void> {\n if (this.wllama) {\n try {\n await (this.wllama as any).exit();\n } catch (_) {\n /* ignore */\n }\n this.wllama = null;\n }\n if (this.modelName) {\n try {\n await this.deleteFromCache(this.modelName);\n } catch (_) {\n /* ignore */\n }\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(\n url,\n new Response(file, {\n headers: { 'Content-Type': 'application/octet-stream' },\n }),\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}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/WllamaService.ts"],"sourcesContent":["// src/WllamaService.ts\n\nimport { Wllama } from \"@wllama/wllama/esm/index.js\";\n\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 = \"\";\n private readonly 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 /**\n * Check WebGPU without directly accessing navigator.gpu.\n *\n * This avoids requiring @webgpu/types merely for feature detection.\n */\n private hasWebGPU(): boolean {\n return typeof navigator !== \"undefined\" && \"gpu\" in navigator;\n }\n\n /** Check browser compatibility and capabilities. */\n checkEnvironment(): ConnectionResult {\n if (\n typeof window === \"undefined\" ||\n typeof navigator === \"undefined\"\n ) {\n return {\n success: false,\n error: \"WllamaService can only run in a browser environment.\",\n };\n }\n\n if (typeof WebAssembly === \"undefined\") {\n return {\n success: false,\n error: \"WebAssembly is not supported in this browser.\",\n };\n }\n\n return {\n success: true,\n hasWebGPU: this.hasWebGPU(),\n crossOriginIsolated: window.crossOriginIsolated,\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 environment = this.checkEnvironment();\n\n if (!environment.success) {\n return {\n success: false,\n error: environment.error,\n };\n }\n\n await this.unload();\n\n try {\n onProgress?.(10);\n\n this.wllama = new Wllama({\n default: this.config.wasmPath,\n });\n\n onProgress?.(30);\n\n const useWebGPU =\n this.hasWebGPU() && this.config.nGpuLayers > 0;\n\n await this.wllama.loadModel([file], {\n n_ctx: this.config.nCtx,\n n_gpu_layers: useWebGPU\n ? this.config.nGpuLayers\n : 0,\n jinja: true,\n });\n\n this.modelName = file.name;\n\n onProgress?.(100);\n\n return {\n success: true,\n usedWebGPU: useWebGPU,\n };\n } catch (error: unknown) {\n await this.unload();\n\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Failed to load model\",\n };\n }\n }\n\n /**\n * Generate a chat response using the model's embedded chat template.\n *\n * Suitable for instruction-tuned models such as Phi-3 Instruct,\n * Llama Instruct, ChatML models, and similar chat models.\n */\n async generate(\n req: GenerateRequest,\n ): Promise<GenerateResult> {\n if (!this.wllama) {\n return {\n success: false,\n error: \"No model loaded. Call loadModel() first.\",\n };\n }\n\n try {\n const startedAt = performance.now();\n\n const messages: Array<{\n role: \"system\" | \"user\";\n content: string;\n }> = [];\n\n if (req.system) {\n messages.push({\n role: \"system\",\n content: req.system,\n });\n }\n\n messages.push({\n role: \"user\",\n content: req.prompt,\n });\n\n /*\n * Keep the cast here if the installed @wllama/wllama declaration\n * does not expose the exact createChatCompletion signature.\n */\n const response = await (\n this.wllama as unknown as {\n createChatCompletion(options: {\n messages: Array<{\n role: \"system\" | \"user\";\n content: string;\n }>;\n stream: false;\n max_tokens: number;\n temperature: number;\n top_k: number;\n top_p: number;\n stop?: string | string[];\n abortSignal?: AbortSignal;\n }): Promise<{\n choices?: Array<{\n message?: {\n content?: string;\n };\n }>;\n }>;\n }\n ).createChatCompletion({\n messages,\n stream: false,\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 stop: req.stop,\n abortSignal: req.abortSignal,\n });\n\n const text =\n response.choices?.[0]?.message?.content ?? \"\";\n\n return {\n success: true,\n text,\n timeMs: Math.round(\n performance.now() - startedAt,\n ),\n };\n } catch (error: unknown) {\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Generation failed\",\n };\n }\n }\n\n /**\n * Generate a raw completion without applying a chat template.\n *\n * Suitable for base completion models, manually formatted prompts,\n * few-shot completion, fill-in-the-middle, and custom templates.\n */\n async generateCompletion(\n req: GenerateRequest,\n ): Promise<GenerateResult> {\n if (!this.wllama) {\n return {\n success: false,\n error: \"No model loaded. Call loadModel() first.\",\n };\n }\n\n try {\n const startedAt = performance.now();\n\n const prompt = req.system\n ? `${req.system}\\n\\n${req.prompt}`\n : req.prompt;\n\n const response =\n await this.wllama.createCompletion({\n prompt,\n stream: false,\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 stop: req.stop,\n abortSignal: req.abortSignal,\n });\n\n const choice = response.choices?.[0];\n\n console.debug(\"[wllama] raw completion\", {\n text: choice?.text,\n finishReason: choice?.finish_reason,\n usage: response.usage,\n timings: response.timings,\n });\n\n return {\n success: true,\n text: choice?.text ?? \"\",\n timeMs: Math.round(\n performance.now() - startedAt,\n ),\n };\n } catch (error: unknown) {\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Generation failed\",\n };\n }\n }\n\n /** Unload the current model and release its resources. */\n async unload(): Promise<void> {\n if (this.wllama) {\n try {\n await this.wllama.exit();\n } catch {\n // Ignore shutdown errors.\n }\n\n this.wllama = null;\n }\n\n this.modelName = \"\";\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}"],"mappings":"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","names":[]}
|