veryfront 0.1.868 → 0.1.869

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.
Files changed (104) hide show
  1. package/esm/cli/commands/demo/steps.js +1 -1
  2. package/esm/cli/commands/dev/command.d.ts.map +1 -1
  3. package/esm/cli/commands/dev/command.js +6 -1
  4. package/esm/cli/commands/start/command.d.ts +12 -0
  5. package/esm/cli/commands/start/command.d.ts.map +1 -1
  6. package/esm/cli/commands/start/command.js +32 -6
  7. package/esm/cli/shared/runtime-auth.d.ts +12 -0
  8. package/esm/cli/shared/runtime-auth.d.ts.map +1 -0
  9. package/esm/cli/shared/runtime-auth.js +34 -0
  10. package/esm/deno.js +1 -1
  11. package/esm/src/agent/ag-ui/handler.d.ts.map +1 -1
  12. package/esm/src/agent/ag-ui/handler.js +17 -5
  13. package/esm/src/agent/react/index.d.ts +1 -1
  14. package/esm/src/agent/react/index.d.ts.map +1 -1
  15. package/esm/src/agent/react/use-chat/index.d.ts +1 -1
  16. package/esm/src/agent/react/use-chat/index.d.ts.map +1 -1
  17. package/esm/src/agent/react/use-chat/types.d.ts +2 -10
  18. package/esm/src/agent/react/use-chat/types.d.ts.map +1 -1
  19. package/esm/src/agent/react/use-chat/use-chat.d.ts +3 -4
  20. package/esm/src/agent/react/use-chat/use-chat.d.ts.map +1 -1
  21. package/esm/src/agent/react/use-chat/use-chat.js +16 -107
  22. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  23. package/esm/src/agent/runtime/index.js +6 -11
  24. package/esm/src/agent/runtime/model-resolution.d.ts +2 -2
  25. package/esm/src/agent/runtime/model-resolution.d.ts.map +1 -1
  26. package/esm/src/agent/runtime/model-resolution.js +59 -13
  27. package/esm/src/agent/types.d.ts +2 -2
  28. package/esm/src/chat/index.d.ts +2 -2
  29. package/esm/src/chat/index.d.ts.map +1 -1
  30. package/esm/src/chat/index.js +1 -1
  31. package/esm/src/embedding/upload-handler.d.ts +2 -1
  32. package/esm/src/embedding/upload-handler.d.ts.map +1 -1
  33. package/esm/src/embedding/upload-handler.js +2 -1
  34. package/esm/src/html/styles-builder/plugin-loader.d.ts.map +1 -1
  35. package/esm/src/html/styles-builder/plugin-loader.js +2 -12
  36. package/esm/src/integrations/feature-flags.d.ts.map +1 -1
  37. package/esm/src/integrations/feature-flags.js +2 -10
  38. package/esm/src/platform/compat/dns.d.ts +6 -0
  39. package/esm/src/platform/compat/dns.d.ts.map +1 -0
  40. package/esm/src/platform/compat/dns.js +34 -0
  41. package/esm/src/platform/compat/opaque-dependency-versions.d.ts +1 -1
  42. package/esm/src/platform/compat/opaque-dependency-versions.js +1 -1
  43. package/esm/src/platform/index.d.ts +1 -0
  44. package/esm/src/platform/index.d.ts.map +1 -1
  45. package/esm/src/platform/index.js +2 -0
  46. package/esm/src/provider/index.d.ts +1 -1
  47. package/esm/src/provider/index.d.ts.map +1 -1
  48. package/esm/src/provider/index.js +1 -1
  49. package/esm/src/provider/local/env.d.ts +3 -0
  50. package/esm/src/provider/local/env.d.ts.map +1 -1
  51. package/esm/src/provider/local/env.js +31 -0
  52. package/esm/src/provider/local/local-engine.d.ts +38 -5
  53. package/esm/src/provider/local/local-engine.d.ts.map +1 -1
  54. package/esm/src/provider/local/local-engine.js +191 -19
  55. package/esm/src/provider/local/model-catalog.d.ts +8 -4
  56. package/esm/src/provider/local/model-catalog.d.ts.map +1 -1
  57. package/esm/src/provider/local/model-catalog.js +36 -41
  58. package/esm/src/provider/local/model-runtime-adapter.js +5 -5
  59. package/esm/src/provider/model-registry.d.ts +2 -12
  60. package/esm/src/provider/model-registry.d.ts.map +1 -1
  61. package/esm/src/provider/model-registry.js +4 -92
  62. package/esm/src/react/components/chat/chat/components/inference-badge.d.ts +2 -3
  63. package/esm/src/react/components/chat/chat/components/inference-badge.d.ts.map +1 -1
  64. package/esm/src/react/components/chat/chat/components/inference-badge.js +3 -31
  65. package/esm/src/react/components/chat/chat/composition/chat-message-list.d.ts +1 -2
  66. package/esm/src/react/components/chat/chat/composition/chat-message-list.d.ts.map +1 -1
  67. package/esm/src/react/components/chat/chat/composition/chat-message-list.js +5 -11
  68. package/esm/src/react/components/chat/chat/index.d.ts +2 -4
  69. package/esm/src/react/components/chat/chat/index.d.ts.map +1 -1
  70. package/esm/src/react/components/chat/chat/index.js +4 -6
  71. package/esm/src/react/components/chat/chat-with-sidebar.d.ts +0 -1
  72. package/esm/src/react/components/chat/chat-with-sidebar.d.ts.map +1 -1
  73. package/esm/src/react/components/chat/chat-with-sidebar.js +0 -1
  74. package/esm/src/react/components/chat/chat.d.ts +1 -1
  75. package/esm/src/react/components/chat/chat.d.ts.map +1 -1
  76. package/esm/src/react/components/chat/chat.js +1 -1
  77. package/esm/src/security/sandbox/worker-egress-guard.d.ts +1 -1
  78. package/esm/src/security/sandbox/worker-egress-guard.d.ts.map +1 -1
  79. package/esm/src/security/sandbox/worker-egress-guard.js +4 -17
  80. package/esm/src/server/handlers/dev/framework-candidates.generated.d.ts.map +1 -1
  81. package/esm/src/server/handlers/dev/framework-candidates.generated.js +3 -54
  82. package/esm/src/utils/version-constant.d.ts +1 -1
  83. package/esm/src/utils/version-constant.js +1 -1
  84. package/esm/src/workflow/worker/dynamic-run-entrypoint.d.ts +3 -2
  85. package/esm/src/workflow/worker/dynamic-run-entrypoint.d.ts.map +1 -1
  86. package/esm/src/workflow/worker/run-entrypoint.d.ts +6 -4
  87. package/esm/src/workflow/worker/run-entrypoint.d.ts.map +1 -1
  88. package/esm/src/workflow/worker/run-entrypoint.js +3 -2
  89. package/package.json +2 -2
  90. package/esm/src/agent/react/use-chat/browser-inference/browser-engine.d.ts +0 -18
  91. package/esm/src/agent/react/use-chat/browser-inference/browser-engine.d.ts.map +0 -1
  92. package/esm/src/agent/react/use-chat/browser-inference/browser-engine.js +0 -62
  93. package/esm/src/agent/react/use-chat/browser-inference/types.d.ts +0 -44
  94. package/esm/src/agent/react/use-chat/browser-inference/types.d.ts.map +0 -1
  95. package/esm/src/agent/react/use-chat/browser-inference/types.js +0 -4
  96. package/esm/src/agent/react/use-chat/browser-inference/worker-client.d.ts +0 -30
  97. package/esm/src/agent/react/use-chat/browser-inference/worker-client.d.ts.map +0 -1
  98. package/esm/src/agent/react/use-chat/browser-inference/worker-client.js +0 -66
  99. package/esm/src/agent/react/use-chat/browser-inference/worker-script.d.ts +0 -8
  100. package/esm/src/agent/react/use-chat/browser-inference/worker-script.d.ts.map +0 -1
  101. package/esm/src/agent/react/use-chat/browser-inference/worker-script.js +0 -97
  102. package/esm/src/react/components/chat/chat/components/upgrade-cta.d.ts +0 -9
  103. package/esm/src/react/components/chat/chat/components/upgrade-cta.d.ts.map +0 -1
  104. package/esm/src/react/components/chat/chat/components/upgrade-cta.js +0 -35
@@ -5,7 +5,7 @@
5
5
  * local LLM inference. Provides lazy model loading and streaming text
6
6
  * generation via async generators.
7
7
  *
8
- * Uses ONNX Runtime for inference with q4 quantization NOT q4f16
8
+ * Uses ONNX Runtime for inference with q4 quantization, not q4f16,
9
9
  * due to a known ONNX bug with f16 LayerNorm on CPU.
10
10
  *
11
11
  * @module provider/local
@@ -14,7 +14,7 @@ import { serverLogger } from "../../utils/index.js";
14
14
  import { createError, toError } from "../../errors/veryfront-error.js";
15
15
  import { importTransformers } from "../../platform/compat/opaque-deps.js";
16
16
  import { DEFAULT_LOCAL_MODEL, resolveLocalModel } from "./model-catalog.js";
17
- import { throwIfLocalAIDisabled } from "./env.js";
17
+ import { getLocalAIDevice, getLocalAIThinkingEnabled, throwIfLocalAIDisabled, } from "./env.js";
18
18
  import { createPipelineCache } from "./pipeline-cache.js";
19
19
  const logger = serverLogger.component("local-llm");
20
20
  /** Default maximum new tokens for local model generation */
@@ -25,7 +25,7 @@ const DEFAULT_MAX_NEW_TOKENS = 512;
25
25
  *
26
26
  * Transformers.js (>=3.x) does not expose a `stop_strings` generate option,
27
27
  * so we decode the running sequence with the tokenizer and match the stop
28
- * strings against the suffix the documented `stopping_criteria` mechanism.
28
+ * strings against the suffix through the documented `stopping_criteria` mechanism.
29
29
  *
30
30
  * Transformers.js passes the full sequence (prompt + generated tokens) to
31
31
  * `_call` on every step. We must scan only the generated suffix: if a system
@@ -101,9 +101,9 @@ export async function getTransformers() {
101
101
  // expected: ONNX runtime not available in some environments (e.g. compiled binaries)
102
102
  throw toError(createError({
103
103
  type: "no_ai_available",
104
- message: "Local AI model unavailable native ONNX Runtime is not supported in this environment " +
105
- "(e.g. compiled binaries). Set VERYFRONT_API_TOKEN and VERYFRONT_PROJECT_SLUG, or " +
106
- "configure OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY to use a cloud provider instead.",
104
+ message: "Local AI model unavailable. Native ONNX Runtime is not supported in this environment " +
105
+ "(e.g. compiled binaries). Run veryfront login, set VERYFRONT_API_TOKEN, or " +
106
+ "configure OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY to use a cloud provider.",
107
107
  }));
108
108
  }
109
109
  // Configure cache directory for model files
@@ -113,6 +113,47 @@ export async function getTransformers() {
113
113
  transformersModule = mod;
114
114
  return mod;
115
115
  }
116
+ async function ensureWebGpuAvailable() {
117
+ if (typeof navigator === "undefined" || !("gpu" in navigator)) {
118
+ throw toError(createError({
119
+ type: "no_ai_available",
120
+ message: "Local AI WebGPU unavailable. This runtime does not expose navigator.gpu. " +
121
+ "Use VERYFRONT_LOCAL_AI_DEVICE=cpu or run in a runtime with WebGPU support.",
122
+ }));
123
+ }
124
+ const gpu = navigator.gpu;
125
+ const adapter = await gpu?.requestAdapter?.();
126
+ if (!adapter) {
127
+ throw toError(createError({
128
+ type: "no_ai_available",
129
+ message: "Local AI WebGPU unavailable. No WebGPU adapter was found. " +
130
+ "Use VERYFRONT_LOCAL_AI_DEVICE=cpu or run on a machine with a supported GPU.",
131
+ }));
132
+ }
133
+ }
134
+ async function getLocalInferenceDevice() {
135
+ const device = getLocalAIDevice();
136
+ if (device === "webgpu") {
137
+ await ensureWebGpuAvailable();
138
+ }
139
+ return device;
140
+ }
141
+ function formatDType(dtype) {
142
+ return typeof dtype === "string" ? dtype : JSON.stringify(dtype);
143
+ }
144
+ function getConditionalModelConstructor(transformers, modelInfo) {
145
+ switch (modelInfo.modelClass) {
146
+ case "gemma4":
147
+ return transformers.Gemma4ForConditionalGeneration;
148
+ case "qwen3_5":
149
+ return transformers.Qwen3_5ForConditionalGeneration;
150
+ default:
151
+ throw toError(createError({
152
+ type: "config",
153
+ message: `Local model "${modelInfo.hfId}" requires a supported conditional-generation model class.`,
154
+ }));
155
+ }
156
+ }
116
157
  /**
117
158
  * Bounded, dedup-aware cache of text-generation pipelines keyed by HuggingFace
118
159
  * model id. Only loads a model on a cold cache miss; concurrent loads of the
@@ -121,10 +162,10 @@ export async function getTransformers() {
121
162
  const textGenerationPipelines = createPipelineCache(async (modelInfo) => {
122
163
  try {
123
164
  const transformers = await getTransformers();
124
- logger.info(`Loading local model: ${modelInfo.hfId} (${modelInfo.dtype}, ~${modelInfo.sizeMB}MB)...`);
165
+ logger.info(`Loading local model: ${modelInfo.hfId} (${formatDType(modelInfo.dtype)}, ${modelInfo.device}, ~${modelInfo.sizeMB}MB)...`);
125
166
  const pipe = (await transformers.pipeline("text-generation", modelInfo.hfId, {
126
167
  dtype: modelInfo.dtype,
127
- device: "cpu",
168
+ device: modelInfo.device,
128
169
  }));
129
170
  logger.info(`Model loaded: ${modelInfo.hfId}`);
130
171
  return pipe;
@@ -140,28 +181,151 @@ const textGenerationPipelines = createPipelineCache(async (modelInfo) => {
140
181
  transformersModule = null;
141
182
  throw toError(createError({
142
183
  type: "no_ai_available",
143
- message: "Local AI model unavailable native ONNX Runtime is not supported in this environment " +
144
- "(e.g. compiled binaries). Set VERYFRONT_API_TOKEN and VERYFRONT_PROJECT_SLUG, or " +
145
- "configure OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY to use a cloud provider instead.",
184
+ message: "Local AI model unavailable. Native ONNX Runtime is not supported in this environment " +
185
+ "(e.g. compiled binaries). Run veryfront login, set VERYFRONT_API_TOKEN, or " +
186
+ "configure OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY to use a cloud provider.",
146
187
  }));
147
188
  }
148
189
  throw error;
149
190
  }
150
191
  });
192
+ const conditionalGenerationRuntimes = createPipelineCache(async (modelInfo) => {
193
+ try {
194
+ const transformers = await getTransformers();
195
+ const ModelClass = getConditionalModelConstructor(transformers, modelInfo);
196
+ logger.info(`Loading local model: ${modelInfo.hfId} (${formatDType(modelInfo.dtype)}, ${modelInfo.device}, ~${modelInfo.sizeMB}MB)...`);
197
+ const [processor, model] = await Promise.all([
198
+ transformers.AutoProcessor.from_pretrained(modelInfo.hfId),
199
+ ModelClass.from_pretrained(modelInfo.hfId, {
200
+ dtype: modelInfo.dtype,
201
+ device: modelInfo.device,
202
+ }),
203
+ ]);
204
+ logger.info(`Model loaded: ${modelInfo.hfId}`);
205
+ return { processor, model };
206
+ }
207
+ catch (error) {
208
+ const msg = error instanceof Error ? error.message : String(error);
209
+ if (msg.includes("onnx") || msg.includes("ONNX") ||
210
+ msg.includes("dlopen") || msg.includes("dynamic linking") ||
211
+ msg.includes("native module") || msg.includes("SharedArrayBuffer")) {
212
+ transformersModule = null;
213
+ throw toError(createError({
214
+ type: "no_ai_available",
215
+ message: "Local AI model unavailable. Native ONNX Runtime is not supported in this environment " +
216
+ "(e.g. compiled binaries). Run veryfront login, set VERYFRONT_API_TOKEN, or " +
217
+ "configure OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY to use a cloud provider.",
218
+ }));
219
+ }
220
+ throw error;
221
+ }
222
+ });
223
+ function getModelCacheKey(modelInfo, device) {
224
+ return `${modelInfo.hfId}:${device}`;
225
+ }
226
+ async function loadLocalRuntime(modelInfo) {
227
+ const device = await getLocalInferenceDevice();
228
+ const loadInfo = { ...modelInfo, device };
229
+ const cacheKey = getModelCacheKey(modelInfo, device);
230
+ return modelInfo.engine === "conditional-generation"
231
+ ? conditionalGenerationRuntimes.load(cacheKey, loadInfo)
232
+ : textGenerationPipelines.load(cacheKey, loadInfo);
233
+ }
234
+ function toConditionalMessages(messages) {
235
+ return messages.map((message) => ({
236
+ role: message.role,
237
+ content: [{ type: "text", text: message.content }],
238
+ }));
239
+ }
240
+ export function buildConditionalGenerateOptions(options, transformers, tokenizer, streamer) {
241
+ const { maxNewTokens = DEFAULT_MAX_NEW_TOKENS, temperature = 0.7, topP, topK, stopSequences, } = options;
242
+ const generateOptions = {
243
+ max_new_tokens: maxNewTokens,
244
+ temperature,
245
+ top_p: topP,
246
+ top_k: topK,
247
+ do_sample: temperature > 0,
248
+ streamer,
249
+ };
250
+ if (stopSequences && stopSequences.length > 0) {
251
+ generateOptions.stopping_criteria = buildStopStringCriteria(transformers, tokenizer, stopSequences);
252
+ }
253
+ return generateOptions;
254
+ }
255
+ export function buildConditionalChatTemplateOptions(modelInfo) {
256
+ return {
257
+ add_generation_prompt: true,
258
+ ...(modelInfo.modelClass === "gemma4" ? { enable_thinking: getLocalAIThinkingEnabled() } : {}),
259
+ };
260
+ }
261
+ async function prepareConditionalInputs(runtime, modelInfo, messages) {
262
+ const prompt = runtime.processor.apply_chat_template(toConditionalMessages(messages), buildConditionalChatTemplateOptions(modelInfo));
263
+ if (modelInfo.modelClass === "gemma4") {
264
+ return await runtime.processor(prompt, undefined, undefined, {
265
+ add_special_tokens: false,
266
+ });
267
+ }
268
+ return await runtime.processor(prompt);
269
+ }
270
+ async function* generateConditionalStream(modelInfo, messages, options) {
271
+ const runtime = await loadLocalRuntime(modelInfo);
272
+ const transformers = await getTransformers();
273
+ const inputs = await prepareConditionalInputs(runtime, modelInfo, messages);
274
+ const tokenQueue = [];
275
+ let resolveWaiting = null;
276
+ let done = false;
277
+ function flushWaiting() {
278
+ if (resolveWaiting) {
279
+ resolveWaiting();
280
+ resolveWaiting = null;
281
+ }
282
+ }
283
+ const streamer = new transformers.TextStreamer(runtime.processor.tokenizer, {
284
+ skip_prompt: true,
285
+ skip_special_tokens: true,
286
+ callback_function: (text) => {
287
+ tokenQueue.push(text);
288
+ flushWaiting();
289
+ },
290
+ });
291
+ const generatePromise = (async () => {
292
+ try {
293
+ await runtime.model.generate({
294
+ ...inputs,
295
+ ...buildConditionalGenerateOptions(options, transformers, runtime.processor.tokenizer, streamer),
296
+ });
297
+ }
298
+ finally {
299
+ done = true;
300
+ flushWaiting();
301
+ }
302
+ })();
303
+ while (true) {
304
+ while (tokenQueue.length > 0) {
305
+ yield tokenQueue.shift();
306
+ }
307
+ if (done)
308
+ break;
309
+ await new Promise((resolve) => {
310
+ resolveWaiting = resolve;
311
+ });
312
+ }
313
+ await generatePromise;
314
+ }
151
315
  /**
152
316
  * Load a text-generation pipeline for the given model.
153
317
  * Returns a cached pipeline if already loaded.
154
318
  */
155
- function loadPipeline(modelInfo) {
156
- return textGenerationPipelines.load(modelInfo.hfId, modelInfo);
319
+ async function loadPipeline(modelInfo) {
320
+ return await loadLocalRuntime(modelInfo);
157
321
  }
158
322
  /**
159
323
  * Eagerly verify that the local AI runtime (@huggingface/transformers + ONNX)
160
324
  * is available by loading the default model pipeline.
161
325
  *
162
326
  * Call this *before* creating the HTTP response stream so that failures surface
163
- * as a thrown error (503) rather than being swallowed inside a ReadableStream
164
- * (200 with in-band SSE error).
327
+ * as a thrown error (503) rather than being swallowed inside a ReadableStream
328
+ * (200 with in-band SSE error).
165
329
  *
166
330
  * In compiled binaries, `import("@huggingface/transformers")` itself fails
167
331
  * because `onnxruntime-node` eagerly `require()`s a native `.node` addon at
@@ -173,7 +337,7 @@ function loadPipeline(modelInfo) {
173
337
  */
174
338
  export async function verifyLocalRuntime(modelId) {
175
339
  const modelInfo = resolveLocalModel(modelId || DEFAULT_LOCAL_MODEL);
176
- await loadPipeline(modelInfo);
340
+ await loadLocalRuntime(modelInfo);
177
341
  }
178
342
  /**
179
343
  * Generate text in a streaming fashion using an async generator.
@@ -182,9 +346,13 @@ export async function verifyLocalRuntime(modelId) {
182
346
  */
183
347
  export async function* generateStream(modelId, messages, options = {}) {
184
348
  const modelInfo = resolveLocalModel(modelId);
349
+ if (modelInfo.engine === "conditional-generation") {
350
+ yield* generateConditionalStream(modelInfo, messages, options);
351
+ return;
352
+ }
185
353
  const pipe = await loadPipeline(modelInfo);
186
354
  const transformers = await getTransformers();
187
- // Use a queue to bridge TextStreamer callbacks async generator
355
+ // Use a queue to bridge TextStreamer callbacks to an async generator.
188
356
  const tokenQueue = [];
189
357
  let resolveWaiting = null;
190
358
  let done = false;
@@ -243,12 +411,16 @@ export async function generate(modelId, messages, options = {}) {
243
411
  */
244
412
  export async function preloadModel(modelId) {
245
413
  const modelInfo = resolveLocalModel(modelId);
246
- await loadPipeline(modelInfo);
414
+ await loadLocalRuntime(modelInfo);
247
415
  }
248
416
  /**
249
417
  * Check if a model is currently loaded in memory.
250
418
  */
251
419
  export function isModelLoaded(modelId) {
252
420
  const modelInfo = resolveLocalModel(modelId);
253
- return textGenerationPipelines.has(modelInfo.hfId);
421
+ const device = getLocalAIDevice();
422
+ const cacheKey = getModelCacheKey(modelInfo, device);
423
+ return modelInfo.engine === "conditional-generation"
424
+ ? conditionalGenerationRuntimes.has(cacheKey)
425
+ : textGenerationPipelines.has(cacheKey);
254
426
  }
@@ -10,7 +10,11 @@ export interface ModelInfo {
10
10
  /** HuggingFace model repository ID */
11
11
  hfId: string;
12
12
  /** Quantization dtype for ONNX Runtime */
13
- dtype: "q4" | "q8" | "fp16" | "fp32";
13
+ dtype: ModelDType | Record<string, ModelDType>;
14
+ /** Runtime path required by the model. */
15
+ engine?: "text-generation" | "conditional-generation";
16
+ /** Transformers.js model class used by conditional-generation models. */
17
+ modelClass?: "gemma4" | "qwen3_5";
14
18
  /** Approximate download size in MB */
15
19
  sizeMB: number;
16
20
  /** Human-readable description */
@@ -18,8 +22,9 @@ export interface ModelInfo {
18
22
  /** Pooling strategy for embedding models (default: "mean") */
19
23
  pooling?: "mean" | "last_token";
20
24
  }
25
+ export type ModelDType = "q4" | "q8" | "q4f16" | "fp16" | "fp32";
21
26
  /** Default model used when no specific model ID is provided */
22
- export declare const DEFAULT_LOCAL_MODEL = "smollm2-135m";
27
+ export declare const DEFAULT_LOCAL_MODEL = "qwen3.5-0.8b";
23
28
  /** Default embedding model used when no specific model ID is provided */
24
29
  export declare const DEFAULT_LOCAL_EMBEDDING_MODEL = "all-MiniLM-L6-v2";
25
30
  /**
@@ -28,8 +33,7 @@ export declare const DEFAULT_LOCAL_EMBEDDING_MODEL = "all-MiniLM-L6-v2";
28
33
  */
29
34
  export declare function resolveLocalEmbeddingModel(modelId: string): ModelInfo;
30
35
  /**
31
- * Resolve a friendly model ID to its HuggingFace model info.
32
- * Falls back to treating the ID as a raw HuggingFace repository ID.
36
+ * Resolve a supported local model ID to its HuggingFace model info.
33
37
  */
34
38
  export declare function resolveLocalModel(modelId: string): ModelInfo;
35
39
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../../../src/src/provider/local/model-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IACrC,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;CACjC;AAyCD,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAmClD,yEAAyE;AACzE,eAAO,MAAM,6BAA6B,qBAAqB,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAUrE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAW5D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C"}
1
+ {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../../../src/src/provider/local/model-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,0CAA0C;IAC1C,MAAM,CAAC,EAAE,iBAAiB,GAAG,wBAAwB,CAAC;IACtD,yEAAyE;IACzE,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAClC,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;CACjC;AAED,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAuCjE,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAmClD,yEAAyE;AACzE,eAAO,MAAM,6BAA6B,qBAAqB,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAUrE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAU5D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C"}
@@ -6,46 +6,45 @@
6
6
  *
7
7
  * @module provider/local
8
8
  */
9
+ import { createError, toError } from "../../errors/veryfront-error.js";
9
10
  /**
10
11
  * Catalog of supported local models.
11
12
  *
12
- * **Important:** Only `q4` quantization is used `q4f16` has a known
13
- * ONNX Runtime bug with LayerNorm on CPU that produces NaN outputs.
13
+ * Keep this list intentionally small. Local model support means the model has
14
+ * been smoke-tested through the Veryfront local runtime.
14
15
  */
15
16
  const MODEL_CATALOG = {
16
- "smollm2-135m": {
17
- hfId: "HuggingFaceTB/SmolLM2-135M-Instruct",
18
- dtype: "q4",
19
- sizeMB: 100,
20
- description: "SmolLM2 135M — fast, lightweight chat model",
21
- },
22
- "smollm2-360m": {
23
- hfId: "HuggingFaceTB/SmolLM2-360M-Instruct",
24
- dtype: "q4",
25
- sizeMB: 250,
26
- description: "SmolLM2 360M better quality, still fast",
17
+ "qwen3.5-0.8b": {
18
+ hfId: "onnx-community/Qwen3.5-0.8B-ONNX",
19
+ dtype: {
20
+ embed_tokens: "q4",
21
+ vision_encoder: "fp16",
22
+ decoder_model_merged: "q4",
23
+ },
24
+ engine: "conditional-generation",
25
+ modelClass: "qwen3_5",
26
+ sizeMB: 900,
27
+ description: "Qwen3.5 0.8B - compact current-generation Qwen model",
27
28
  },
28
- "smollm2-1.7b": {
29
- hfId: "HuggingFaceTB/SmolLM2-1.7B-Instruct",
29
+ "gemma4-e2b-it": {
30
+ hfId: "onnx-community/gemma-4-E2B-it-ONNX",
30
31
  dtype: "q4",
31
- sizeMB: 1000,
32
- description: "SmolLM2 1.7B — highest quality local model",
32
+ engine: "conditional-generation",
33
+ modelClass: "gemma4",
34
+ sizeMB: 1800,
35
+ description: "Gemma 4 E2B IT - compact current-generation Gemma model",
33
36
  },
34
- "qwen3-1.7b": {
35
- hfId: "onnx-community/Qwen3-1.7B-ONNX",
37
+ "gemma4-e4b-it": {
38
+ hfId: "onnx-community/gemma-4-E4B-it-ONNX",
36
39
  dtype: "q4",
37
- sizeMB: 1200,
38
- description: "Qwen3 1.7B — strong multilingual reasoning and tool use",
39
- },
40
- "gemma3-1b": {
41
- hfId: "onnx-community/gemma-3-1b-it-ONNX",
42
- dtype: "q4",
43
- sizeMB: 700,
44
- description: "Gemma 3 1B — Google's compact instruction-tuned model",
40
+ engine: "conditional-generation",
41
+ modelClass: "gemma4",
42
+ sizeMB: 6000,
43
+ description: "Gemma 4 E4B IT - larger current-generation Gemma model",
45
44
  },
46
45
  };
47
46
  /** Default model used when no specific model ID is provided */
48
- export const DEFAULT_LOCAL_MODEL = "smollm2-135m";
47
+ export const DEFAULT_LOCAL_MODEL = "qwen3.5-0.8b";
49
48
  /**
50
49
  * Catalog of supported local embedding models.
51
50
  *
@@ -56,26 +55,26 @@ const EMBEDDING_MODEL_CATALOG = {
56
55
  hfId: "Xenova/all-MiniLM-L6-v2",
57
56
  dtype: "q4",
58
57
  sizeMB: 23,
59
- description: "All-MiniLM-L6-v2 fast 384-dim embeddings",
58
+ description: "All-MiniLM-L6-v2 - fast 384-dim embeddings",
60
59
  },
61
60
  "nomic-embed-text-v1.5": {
62
61
  hfId: "nomic-ai/nomic-embed-text-v1.5",
63
62
  dtype: "q4",
64
63
  sizeMB: 130,
65
- description: "Nomic Embed Text v1.5 768-dim, variable-length embeddings",
64
+ description: "Nomic Embed Text v1.5 - 768-dim, variable-length embeddings",
66
65
  },
67
66
  "bge-base-en-v1.5": {
68
67
  hfId: "Xenova/bge-base-en-v1.5",
69
68
  dtype: "q4",
70
69
  sizeMB: 110,
71
- description: "BGE Base EN v1.5 768-dim, strong English embeddings",
70
+ description: "BGE Base EN v1.5 - 768-dim, strong English embeddings",
72
71
  },
73
72
  "qwen3-embedding-0.6b": {
74
73
  hfId: "onnx-community/Qwen3-Embedding-0.6B-ONNX",
75
74
  dtype: "q8",
76
75
  sizeMB: 620,
77
76
  pooling: "last_token",
78
- description: "Qwen3 Embedding 0.6B SOTA multilingual embeddings",
77
+ description: "Qwen3 Embedding 0.6B - SOTA multilingual embeddings",
79
78
  },
80
79
  };
81
80
  /** Default embedding model used when no specific model ID is provided */
@@ -96,20 +95,16 @@ export function resolveLocalEmbeddingModel(modelId) {
96
95
  };
97
96
  }
98
97
  /**
99
- * Resolve a friendly model ID to its HuggingFace model info.
100
- * Falls back to treating the ID as a raw HuggingFace repository ID.
98
+ * Resolve a supported local model ID to its HuggingFace model info.
101
99
  */
102
100
  export function resolveLocalModel(modelId) {
103
101
  const catalogEntry = MODEL_CATALOG[modelId];
104
102
  if (catalogEntry)
105
103
  return catalogEntry;
106
- // Treat as raw HuggingFace model ID (e.g. "HuggingFaceTB/SmolLM2-135M-Instruct")
107
- return {
108
- hfId: modelId,
109
- dtype: "q4",
110
- sizeMB: 0,
111
- description: `Custom model: ${modelId}`,
112
- };
104
+ throw toError(createError({
105
+ type: "config",
106
+ message: `Unsupported local model "${modelId}". Supported local models: ${getLocalModelIds().join(", ")}.`,
107
+ }));
113
108
  }
114
109
  /**
115
110
  * Get all available local model IDs.
@@ -24,7 +24,7 @@ const DEFAULT_MAX_NEW_TOKENS = 512;
24
24
  function convertPrompt(prompt) {
25
25
  const messages = [];
26
26
  for (const msg of prompt) {
27
- // Skip tool messages local models don't support tool calling
27
+ // Skip tool messages. Local models do not support tool calling.
28
28
  if (msg.role === "tool")
29
29
  continue;
30
30
  const mappedRole = msg.role === "system"
@@ -79,7 +79,7 @@ export function createLocalModel(modelId) {
79
79
  async doGenerate(options) {
80
80
  const messages = convertPrompt(options.prompt);
81
81
  const genOptions = toGenerateOptions(options);
82
- logger.debug(`[local] doGenerate: ${messages.length} messages ${resolvedId}`);
82
+ logger.debug(`[local] doGenerate: ${messages.length} messages -> ${resolvedId}`);
83
83
  const text = await generate(resolvedId, messages, genOptions);
84
84
  return {
85
85
  content: [{ type: "text", text }],
@@ -93,7 +93,7 @@ export function createLocalModel(modelId) {
93
93
  };
94
94
  },
95
95
  async doStream(options) {
96
- // Eagerly check if local AI is disabled must throw before creating the
96
+ // Eagerly check if local AI is disabled. This must throw before creating the
97
97
  // ReadableStream, otherwise the 200 response headers are already committed.
98
98
  // Note: getTransformers() in local-engine.ts also checks this, but we need
99
99
  // the check here too because doStream creates a ReadableStream wrapper and
@@ -101,7 +101,7 @@ export function createLocalModel(modelId) {
101
101
  throwIfLocalAIDisabled();
102
102
  const messages = convertPrompt(options.prompt);
103
103
  const genOptions = toGenerateOptions(options);
104
- logger.debug(`[local] doStream: ${messages.length} messages ${resolvedId}`);
104
+ logger.debug(`[local] doStream: ${messages.length} messages -> ${resolvedId}`);
105
105
  const textId = `text-${Date.now()}`;
106
106
  const stream = new ReadableStream({
107
107
  async start(controller) {
@@ -140,7 +140,7 @@ export function createLocalModel(modelId) {
140
140
  controller.close();
141
141
  }
142
142
  catch (error) {
143
- // Let no_ai_available propagate the chat handler needs it
143
+ // Let no_ai_available propagate. The chat handler needs it
144
144
  // for a proper 503 response instead of a 200 with in-band error.
145
145
  const vfError = fromError(error);
146
146
  if (vfError?.type === "no_ai_available")
@@ -24,16 +24,6 @@ export type ModelProviderFactory = (modelId: string) => ModelRuntime;
24
24
  * ```
25
25
  */
26
26
  export declare function registerModelProvider(name: string, factory: ModelProviderFactory): void;
27
- /**
28
- * Find the first cloud provider with a valid API key.
29
- *
30
- * Returns a "provider/model" string that can be passed to `resolveModel`,
31
- * or `null` if no cloud provider is available.
32
- *
33
- * This is intentionally a **query** — it does NOT resolve the model.
34
- * The caller (agent runtime) decides whether to use it.
35
- */
36
- export declare function findAvailableCloudModel(): string | null;
37
27
  /**
38
28
  * Resolve a "provider/model" string to a framework-compatible model runtime.
39
29
  *
@@ -59,8 +49,8 @@ export declare function getRegisteredModelProviders(): string[];
59
49
  * For real local-engine models (created by `createLocalModel()`) this
60
50
  * eagerly loads the ONNX pipeline to surface `no_ai_available` errors
61
51
  * **before** the HTTP response stream is created. Must happen before the
62
- * ReadableStream so the chat handler can return a proper 503 (with
63
- * browser-fallback info) rather than a 200 with an in-band SSE error.
52
+ * ReadableStream so the chat handler can return a proper 503 rather than a
53
+ * 200 with an in-band SSE error.
64
54
  *
65
55
  * Uses the `_isVfLocalModel` marker set by `createLocalModel()` to
66
56
  * distinguish real local-engine models from mock/custom providers that
@@ -1 +1 @@
1
- {"version":3,"file":"model-registry.d.ts","sourceRoot":"","sources":["../../../src/src/provider/model-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAyBH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,sDAAsD;AACtD,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,YAAY,CAAC;AAOrE;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,IAAI,CAEN;AAgMD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAWvD;AAMD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CA6D9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,EAAE,CAGtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
1
+ {"version":3,"file":"model-registry.d.ts","sourceRoot":"","sources":["../../../src/src/provider/model-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAeH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,sDAAsD;AACtD,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,YAAY,CAAC;AAOrE;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,IAAI,CAEN;AAsJD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAuC9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,EAAE,CAGtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}