wllama-service 2.0.3 → 2.0.5

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 CHANGED
@@ -41,8 +41,30 @@ declare class WllamaService {
41
41
  checkEnvironment(): ConnectionResult;
42
42
  /** Load a GGUF model file from the user's filesystem */
43
43
  loadModel(file: File, onProgress?: (progress: number) => void): Promise<LoadModelResult>;
44
- /** Generate a response from the loaded model */
44
+ /**
45
+ * Generate a response using the model's chat template.
46
+ * Use this for normal conversational / instruction-following use cases —
47
+ * it accepts a `messages`-style request (system + user) and
48
+ * createChatCompletion formats it with the model's real chat template
49
+ * (ChatML, Llama-3, etc.) before running inference.
50
+ */
45
51
  generate(req: GenerateRequest): Promise<GenerateResult>;
52
+ /**
53
+ * Generate a raw completion with no chat-template formatting.
54
+ * Use this when you need to control the exact prompt string yourself —
55
+ * e.g. few-shot prompts, base-model completion, custom templates, or
56
+ * fill-in-the-middle style prompts.
57
+ *
58
+ * createCompletion (confirmed against @wllama/wllama@3.5.1's real type
59
+ * defs) takes a single options object with a raw `prompt` string. It does
60
+ * NOT accept a `messages` array and does NOT apply any chat template —
61
+ * whatever string you pass is exactly what the model sees. `req.system`
62
+ * (if provided) is prepended as plain text; it is your responsibility to
63
+ * format it however the target model expects (call
64
+ * `this.wllama.getChatTemplate()` if you want to match the model's
65
+ * native template manually).
66
+ */
67
+ generateCompletion(req: GenerateRequest): Promise<GenerateResult>;
46
68
  /** Unload the current model and free memory */
47
69
  unload(): Promise<void>;
48
70
  /** Whether a model is currently loaded */
package/dist/index.d.ts CHANGED
@@ -41,8 +41,30 @@ declare class WllamaService {
41
41
  checkEnvironment(): ConnectionResult;
42
42
  /** Load a GGUF model file from the user's filesystem */
43
43
  loadModel(file: File, onProgress?: (progress: number) => void): Promise<LoadModelResult>;
44
- /** Generate a response from the loaded model */
44
+ /**
45
+ * Generate a response using the model's chat template.
46
+ * Use this for normal conversational / instruction-following use cases —
47
+ * it accepts a `messages`-style request (system + user) and
48
+ * createChatCompletion formats it with the model's real chat template
49
+ * (ChatML, Llama-3, etc.) before running inference.
50
+ */
45
51
  generate(req: GenerateRequest): Promise<GenerateResult>;
52
+ /**
53
+ * Generate a raw completion with no chat-template formatting.
54
+ * Use this when you need to control the exact prompt string yourself —
55
+ * e.g. few-shot prompts, base-model completion, custom templates, or
56
+ * fill-in-the-middle style prompts.
57
+ *
58
+ * createCompletion (confirmed against @wllama/wllama@3.5.1's real type
59
+ * defs) takes a single options object with a raw `prompt` string. It does
60
+ * NOT accept a `messages` array and does NOT apply any chat template —
61
+ * whatever string you pass is exactly what the model sees. `req.system`
62
+ * (if provided) is prepended as plain text; it is your responsibility to
63
+ * format it however the target model expects (call
64
+ * `this.wllama.getChatTemplate()` if you want to match the model's
65
+ * native template manually).
66
+ */
67
+ generateCompletion(req: GenerateRequest): Promise<GenerateResult>;
46
68
  /** Unload the current model and free memory */
47
69
  unload(): Promise<void>;
48
70
  /** Whether a model is currently loaded */
package/dist/index.js CHANGED
@@ -78,7 +78,13 @@ var WllamaService = class {
78
78
  return { success: false, error: message };
79
79
  }
80
80
  }
81
- /** Generate a response from the loaded model */
81
+ /**
82
+ * Generate a response using the model's chat template.
83
+ * Use this for normal conversational / instruction-following use cases —
84
+ * it accepts a `messages`-style request (system + user) and
85
+ * createChatCompletion formats it with the model's real chat template
86
+ * (ChatML, Llama-3, etc.) before running inference.
87
+ */
82
88
  async generate(req) {
83
89
  if (!this.wllama) {
84
90
  return { success: false, error: "No model loaded. Call loadModel() first." };
@@ -90,6 +96,7 @@ var WllamaService = class {
90
96
  messages.push({ role: "user", content: req.prompt });
91
97
  const response = await this.wllama.createChatCompletion({
92
98
  messages,
99
+ stream: false,
93
100
  max_tokens: req.maxTokens ?? 512,
94
101
  temperature: req.temperature ?? 0.7,
95
102
  top_k: req.topK ?? 40,
@@ -106,6 +113,49 @@ var WllamaService = class {
106
113
  return { success: false, error: message };
107
114
  }
108
115
  }
116
+ /**
117
+ * Generate a raw completion with no chat-template formatting.
118
+ * Use this when you need to control the exact prompt string yourself —
119
+ * e.g. few-shot prompts, base-model completion, custom templates, or
120
+ * fill-in-the-middle style prompts.
121
+ *
122
+ * createCompletion (confirmed against @wllama/wllama@3.5.1's real type
123
+ * defs) takes a single options object with a raw `prompt` string. It does
124
+ * NOT accept a `messages` array and does NOT apply any chat template —
125
+ * whatever string you pass is exactly what the model sees. `req.system`
126
+ * (if provided) is prepended as plain text; it is your responsibility to
127
+ * format it however the target model expects (call
128
+ * `this.wllama.getChatTemplate()` if you want to match the model's
129
+ * native template manually).
130
+ */
131
+ async generateCompletion(req) {
132
+ if (!this.wllama) {
133
+ return { success: false, error: "No model loaded. Call loadModel() first." };
134
+ }
135
+ try {
136
+ const t0 = performance.now();
137
+ const prompt = req.system ? `${req.system}
138
+
139
+ ${req.prompt}` : req.prompt;
140
+ const response = await this.wllama.createCompletion({
141
+ prompt,
142
+ stream: false,
143
+ max_tokens: req.maxTokens ?? 512,
144
+ temperature: req.temperature ?? 0.7,
145
+ top_k: req.topK ?? 40,
146
+ top_p: req.topP ?? 0.95
147
+ });
148
+ const text = response?.choices?.[0]?.text ?? "";
149
+ return {
150
+ success: true,
151
+ text,
152
+ timeMs: Math.round(performance.now() - t0)
153
+ };
154
+ } catch (e) {
155
+ const message = e instanceof Error ? e.message : "Generation failed";
156
+ return { success: false, error: message };
157
+ }
158
+ }
109
159
  /** Unload the current model and free memory */
110
160
  async unload() {
111
161
  if (this.wllama) {
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';","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 /**\n * Generate a response using the model's chat template.\n * Use this for normal conversational / instruction-following use cases —\n * it accepts a `messages`-style request (system + user) and\n * createChatCompletion formats it with the model's real chat template\n * (ChatML, Llama-3, etc.) before running inference.\n */\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 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 });\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 /**\n * Generate a raw completion with no chat-template formatting.\n * Use this when you need to control the exact prompt string yourself —\n * e.g. few-shot prompts, base-model completion, custom templates, or\n * fill-in-the-middle style prompts.\n *\n * createCompletion (confirmed against @wllama/wllama@3.5.1's real type\n * defs) takes a single options object with a raw `prompt` string. It does\n * NOT accept a `messages` array and does NOT apply any chat template —\n * whatever string you pass is exactly what the model sees. `req.system`\n * (if provided) is prepended as plain text; it is your responsibility to\n * format it however the target model expects (call\n * `this.wllama.getChatTemplate()` if you want to match the model's\n * native template manually).\n */\n async generateCompletion(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 prompt = req.system ? `${req.system}\\n\\n${req.prompt}` : req.prompt;\n\n const response = await (this.wllama as any).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 });\n\n const text: string = response?.choices?.[0]?.text ?? '';\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":[]}
package/dist/index.mjs CHANGED
@@ -52,7 +52,13 @@ var WllamaService = class {
52
52
  return { success: false, error: message };
53
53
  }
54
54
  }
55
- /** Generate a response from the loaded model */
55
+ /**
56
+ * Generate a response using the model's chat template.
57
+ * Use this for normal conversational / instruction-following use cases —
58
+ * it accepts a `messages`-style request (system + user) and
59
+ * createChatCompletion formats it with the model's real chat template
60
+ * (ChatML, Llama-3, etc.) before running inference.
61
+ */
56
62
  async generate(req) {
57
63
  if (!this.wllama) {
58
64
  return { success: false, error: "No model loaded. Call loadModel() first." };
@@ -64,6 +70,7 @@ var WllamaService = class {
64
70
  messages.push({ role: "user", content: req.prompt });
65
71
  const response = await this.wllama.createChatCompletion({
66
72
  messages,
73
+ stream: false,
67
74
  max_tokens: req.maxTokens ?? 512,
68
75
  temperature: req.temperature ?? 0.7,
69
76
  top_k: req.topK ?? 40,
@@ -80,6 +87,49 @@ var WllamaService = class {
80
87
  return { success: false, error: message };
81
88
  }
82
89
  }
90
+ /**
91
+ * Generate a raw completion with no chat-template formatting.
92
+ * Use this when you need to control the exact prompt string yourself —
93
+ * e.g. few-shot prompts, base-model completion, custom templates, or
94
+ * fill-in-the-middle style prompts.
95
+ *
96
+ * createCompletion (confirmed against @wllama/wllama@3.5.1's real type
97
+ * defs) takes a single options object with a raw `prompt` string. It does
98
+ * NOT accept a `messages` array and does NOT apply any chat template —
99
+ * whatever string you pass is exactly what the model sees. `req.system`
100
+ * (if provided) is prepended as plain text; it is your responsibility to
101
+ * format it however the target model expects (call
102
+ * `this.wllama.getChatTemplate()` if you want to match the model's
103
+ * native template manually).
104
+ */
105
+ async generateCompletion(req) {
106
+ if (!this.wllama) {
107
+ return { success: false, error: "No model loaded. Call loadModel() first." };
108
+ }
109
+ try {
110
+ const t0 = performance.now();
111
+ const prompt = req.system ? `${req.system}
112
+
113
+ ${req.prompt}` : req.prompt;
114
+ const response = await this.wllama.createCompletion({
115
+ prompt,
116
+ stream: false,
117
+ max_tokens: req.maxTokens ?? 512,
118
+ temperature: req.temperature ?? 0.7,
119
+ top_k: req.topK ?? 40,
120
+ top_p: req.topP ?? 0.95
121
+ });
122
+ const text = response?.choices?.[0]?.text ?? "";
123
+ return {
124
+ success: true,
125
+ text,
126
+ timeMs: Math.round(performance.now() - t0)
127
+ };
128
+ } catch (e) {
129
+ const message = e instanceof Error ? e.message : "Generation failed";
130
+ return { success: false, error: message };
131
+ }
132
+ }
83
133
  /** Unload the current model and free memory */
84
134
  async unload() {
85
135
  if (this.wllama) {
@@ -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":["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 /**\n * Generate a response using the model's chat template.\n * Use this for normal conversational / instruction-following use cases —\n * it accepts a `messages`-style request (system + user) and\n * createChatCompletion formats it with the model's real chat template\n * (ChatML, Llama-3, etc.) before running inference.\n */\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 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 });\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 /**\n * Generate a raw completion with no chat-template formatting.\n * Use this when you need to control the exact prompt string yourself —\n * e.g. few-shot prompts, base-model completion, custom templates, or\n * fill-in-the-middle style prompts.\n *\n * createCompletion (confirmed against @wllama/wllama@3.5.1's real type\n * defs) takes a single options object with a raw `prompt` string. It does\n * NOT accept a `messages` array and does NOT apply any chat template —\n * whatever string you pass is exactly what the model sees. `req.system`\n * (if provided) is prepended as plain text; it is your responsibility to\n * format it however the target model expects (call\n * `this.wllama.getChatTemplate()` if you want to match the model's\n * native template manually).\n */\n async generateCompletion(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 prompt = req.system ? `${req.system}\\n\\n${req.prompt}` : req.prompt;\n\n const response = await (this.wllama as any).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 });\n\n const text: string = response?.choices?.[0]?.text ?? '';\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":";AAAA,SAAS,cAAc;AAShB,IAAM,gBAAN,MAAoB;AAAA,EAKzB,YAAY,SAA8B,CAAC,GAAG;AAJ9C,SAAQ,SAAwB;AAChC,SAAQ,YAAoB;AAI1B,SAAK,SAAS;AAAA,MACZ,UAAU,OAAO,YAAY;AAAA,MAC7B,YAAY,OAAO,cAAc;AAAA,MACjC,MAAM,OAAO,QAAQ;AAAA,IACvB;AAAA,EACF;AAAA;AAAA,EAGA,mBAAqC;AACnC,QAAI,OAAO,gBAAgB,aAAa;AACtC,aAAO,EAAE,SAAS,OAAO,OAAO,gDAAgD;AAAA,IAClF;AACA,QAAI,EAAE,YAAY,SAAS;AACzB,aAAO,EAAE,SAAS,OAAO,OAAO,8CAA8C;AAAA,IAChF;AACA,UAAM,YAAY,CAAC,CAAE,UAAkB;AACvC,UAAM,WAAW,OAAO;AACxB,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,qBAAqB;AAAA,IACvB;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,UACJ,MACA,YAC0B;AAC1B,UAAM,MAAM,KAAK,iBAAiB;AAClC,QAAI,CAAC,IAAI,QAAS,QAAO,EAAE,SAAS,OAAO,OAAO,IAAI,MAAM;AAE5D,UAAM,KAAK,OAAO;AAElB,QAAI;AACF,mBAAa,EAAE;AAMf,YAAM,cAAc,EAAE,SAAS,KAAK,OAAO,SAAS;AACpD,WAAK,SAAS,IAAI,OAAO,WAAW;AACpC,mBAAa,EAAE;AAEf,YAAM,KAAK,aAAa,IAAI;AAC5B,mBAAa,EAAE;AAEf,YAAM,YAAY,CAAC,CAAE,UAAkB;AACvC,YAAO,KAAK,OAAe,UAAU,CAAC,IAAI,GAAG;AAAA,QAC3C,OAAO,KAAK,OAAO;AAAA,QACnB,GAAI,aAAa,KAAK,OAAO,aAAa,IACtC,EAAE,cAAc,KAAK,OAAO,WAAW,IACvC,CAAC;AAAA,MACP,CAAC;AAED,WAAK,YAAY,KAAK;AACtB,mBAAa,GAAG;AAEhB,aAAO,EAAE,SAAS,MAAM,YAAY,UAAU;AAAA,IAChD,SAAS,GAAY;AACnB,YAAM,KAAK,OAAO;AAClB,YAAM,UAAU,aAAa,QAAQ,EAAE,UAAU;AACjD,aAAO,EAAE,SAAS,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,SAAS,KAA+C;AAC5D,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,EAAE,SAAS,OAAO,OAAO,2CAA2C;AAAA,IAC7E;AAEA,QAAI;AACF,YAAM,KAAK,YAAY,IAAI;AAE3B,YAAM,WAAgD,CAAC;AACvD,UAAI,IAAI,OAAQ,UAAS,KAAK,EAAE,MAAM,UAAU,SAAS,IAAI,OAAO,CAAC;AACrE,eAAS,KAAK,EAAE,MAAM,QAAQ,SAAS,IAAI,OAAO,CAAC;AAEnD,YAAM,WAAW,MAAO,KAAK,OAAe,qBAAqB;AAAA,QAC/D;AAAA,QACA,QAAQ;AAAA,QACR,YAAY,IAAI,aAAa;AAAA,QAC7B,aAAa,IAAI,eAAe;AAAA,QAChC,OAAO,IAAI,QAAQ;AAAA,QACnB,OAAO,IAAI,QAAQ;AAAA,MACrB,CAAC;AAED,YAAM,OAAe,UAAU,UAAU,CAAC,GAAG,SAAS,WAAW;AACjE,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA,QAAQ,KAAK,MAAM,YAAY,IAAI,IAAI,EAAE;AAAA,MAC3C;AAAA,IACF,SAAS,GAAY;AACnB,YAAM,UAAU,aAAa,QAAQ,EAAE,UAAU;AACjD,aAAO,EAAE,SAAS,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,mBAAmB,KAA+C;AACtE,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,EAAE,SAAS,OAAO,OAAO,2CAA2C;AAAA,IAC7E;AAEA,QAAI;AACF,YAAM,KAAK,YAAY,IAAI;AAE3B,YAAM,SAAS,IAAI,SAAS,GAAG,IAAI,MAAM;AAAA;AAAA,EAAO,IAAI,MAAM,KAAK,IAAI;AAEnE,YAAM,WAAW,MAAO,KAAK,OAAe,iBAAiB;AAAA,QAC3D;AAAA,QACA,QAAQ;AAAA,QACR,YAAY,IAAI,aAAa;AAAA,QAC7B,aAAa,IAAI,eAAe;AAAA,QAChC,OAAO,IAAI,QAAQ;AAAA,QACnB,OAAO,IAAI,QAAQ;AAAA,MACrB,CAAC;AAED,YAAM,OAAe,UAAU,UAAU,CAAC,GAAG,QAAQ;AACrD,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA,QAAQ,KAAK,MAAM,YAAY,IAAI,IAAI,EAAE;AAAA,MAC3C;AAAA,IACF,SAAS,GAAY;AACnB,YAAM,UAAU,aAAa,QAAQ,EAAE,UAAU;AACjD,aAAO,EAAE,SAAS,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,SAAwB;AAC5B,QAAI,KAAK,QAAQ;AACf,UAAI;AACF,cAAO,KAAK,OAAe,KAAK;AAAA,MAClC,SAAS,GAAG;AAAA,MAEZ;AACA,WAAK,SAAS;AAAA,IAChB;AACA,QAAI,KAAK,WAAW;AAClB,UAAI;AACF,cAAM,KAAK,gBAAgB,KAAK,SAAS;AAAA,MAC3C,SAAS,GAAG;AAAA,MAEZ;AACA,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,WAAoB;AACtB,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,eAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,aAAa,MAA2B;AACpD,UAAM,MAAM,GAAG,OAAO,SAAS,MAAM,wBAAwB,KAAK,IAAI;AACtE,UAAM,QAAQ,MAAM,OAAO,KAAK,qBAAqB;AACrD,UAAM,MAAM;AAAA,MACV;AAAA,MACA,IAAI,SAAS,MAAM;AAAA,QACjB,SAAS,EAAE,gBAAgB,2BAA2B;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,gBAAgB,UAAiC;AAC7D,UAAM,MAAM,GAAG,OAAO,SAAS,MAAM,wBAAwB,QAAQ;AACrE,UAAM,QAAQ,MAAM,OAAO,KAAK,qBAAqB;AACrD,UAAM,MAAM,OAAO,GAAG;AAAA,EACxB;AACF;","names":[]}
package/package.json CHANGED
@@ -1,11 +1,8 @@
1
1
  {
2
2
  "name": "wllama-service",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "Framework-agnostic browser LLM service wrapper with WebGPU and WebAssembly support",
5
5
  "main": "./dist/index.js",
6
- "bin": {
7
- "copy-wllama-wasm": "./scripts/postinstall.js"
8
- },
9
6
  "module": "./dist/index.mjs",
10
7
  "types": "./dist/index.d.ts",
11
8
  "exports": {
@@ -20,7 +17,6 @@
20
17
  "README.md"
21
18
  ],
22
19
  "scripts": {
23
- "postinstall": "node scripts/postinstall.js",
24
20
  "build": "tsup",
25
21
  "dev": "tsup --watch",
26
22
  "typecheck": "tsc --noEmit",
@@ -1,53 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('fs');
5
- const path = require('path');
6
-
7
- /**
8
- * Resolve @wllama/wllama's wasm file using Node's real module resolution
9
- * instead of a guessed relative path. This works correctly regardless of
10
- * where in the yarn workspaces hoisting tree @wllama/wllama actually
11
- * landed (monorepo root, nested under wllama-service, etc) — Node's own
12
- * require.resolve algorithm finds it the same way `require()` would.
13
- */
14
- function resolveWasmSrc() {
15
- const pkgJsonPath = require.resolve('@wllama/wllama/package.json', {
16
- paths: [process.cwd()],
17
- });
18
- const pkgRoot = path.dirname(pkgJsonPath);
19
-
20
- // Different @wllama/wllama versions ship different layouts:
21
- // older: esm/wasm/wllama.wasm
22
- // newer: esm/single-thread/wllama.wasm (+ esm/multi-thread/wllama.wasm)
23
- const candidates = [
24
- path.join(pkgRoot, 'esm', 'wasm', 'wllama.wasm'),
25
- path.join(pkgRoot, 'esm', 'single-thread', 'wllama.wasm'),
26
- ];
27
- const found = candidates.find((p) => fs.existsSync(p));
28
- if (!found) {
29
- throw new Error(
30
- `wllama.wasm not found in any known location under ${pkgRoot}\n` +
31
- `Checked: ${candidates.join(', ')}`,
32
- );
33
- }
34
- return found;
35
- }
36
-
37
- // Destination: this app's own public folder. Adjust this path if your
38
- // app's public folder lives somewhere other than apps/<this-app>/public
39
- // relative to where this script sits.
40
- const destDir = path.resolve(__dirname, '..', 'public', 'wllama');
41
-
42
- let wasmSrc;
43
- try {
44
- wasmSrc = resolveWasmSrc();
45
- } catch (err) {
46
- console.warn('[copy-wllama-wasm] ' + err.message);
47
- process.exit(0);
48
- }
49
-
50
- fs.mkdirSync(destDir, { recursive: true });
51
- const dest = path.join(destDir, 'wllama.wasm');
52
- fs.copyFileSync(wasmSrc, dest);
53
- console.log(`[copy-wllama-wasm] ✓ Copied ${wasmSrc} -> ${dest}`);