From a856d55d8e4f7f6385436bda888d00eda0e08a4b Mon Sep 17 00:00:00 2001 From: jakobhoeg Date: Wed, 4 Feb 2026 18:16:45 +0100 Subject: [PATCH 1/8] docs: add Vercel AI SDK integration with browser-ai/transformers-js package --- .../docs/source/guides/vercel-ai-sdk.md | 285 ++++++++++++ .../docs/source/tutorials/next-ai-sdk.md | 436 ++++++++++++++++++ 2 files changed, 721 insertions(+) create mode 100644 packages/transformers/docs/source/guides/vercel-ai-sdk.md create mode 100644 packages/transformers/docs/source/tutorials/next-ai-sdk.md diff --git a/packages/transformers/docs/source/guides/vercel-ai-sdk.md b/packages/transformers/docs/source/guides/vercel-ai-sdk.md new file mode 100644 index 000000000..15335d8fb --- /dev/null +++ b/packages/transformers/docs/source/guides/vercel-ai-sdk.md @@ -0,0 +1,285 @@ +# Using Transformers.js with the Vercel AI SDK + +[Vercel AI SDK](https://ai-sdk.dev/) is a popular toolkit for building AI-powered applications. With [`@browser-ai/transformers-js`](https://www.browser-ai.dev/docs/ai-sdk-v6/transformers-js), you can use Transformers.js as a model provider for the AI SDK, enabling in-browser (and server-side) inference with a clean, declarative API. + +This guide covers the core concepts and API patterns. For a full step-by-step project walkthrough, see the [Building a Next.js AI Chatbot](../tutorials/next-ai-sdk) tutorial. + +## Why use the Vercel AI SDK with Transformers.js? + +Building functional and production-ready applications using Hugging Face models in the browser with just `@huggingface/transformers` requires significant boilerplate: Web Worker setup, message passing, progress tracking, streaming, interrupt handling, error states, and state management. The `@browser-ai/transformers-js` provider abstracts all of this away, letting you use the same `streamText`, `generateText`, and `useChat` APIs you'd use with any other AI SDK provider. Read more about this [here](https://www.browser-ai.dev/docs/ai-sdk-v6/transformers-js/why). + +## Installation + +```bash +npm install @browser-ai/transformers-js @huggingface/transformers ai @ai-sdk/react +``` + +| @browser-ai/transformers-js | AI SDK | Notes | +|---|---|---| +| v2.0.0+ | v6.x | Current stable | +| v1.0.0 | v5.x | Legacy | + +## Text generation + +### Streaming text + +```js +import { streamText } from "ai"; +import { transformersJS } from "@browser-ai/transformers-js"; + +const result = streamText({ + model: transformersJS("HuggingFaceTB/SmolLM2-360M-Instruct"), + prompt: "Invent a new holiday and describe its traditions.", +}); + +for await (const textPart of result.textStream) { + console.log(textPart); +} +``` + +### Non-streaming text + +```js +import { generateText } from "ai"; +import { transformersJS } from "@browser-ai/transformers-js"; + +const result = await generateText({ + model: transformersJS("HuggingFaceTB/SmolLM2-360M-Instruct"), + prompt: "Invent a new holiday and describe its traditions.", +}); +console.log(result.text); +``` + +## Text embeddings + +```js +import { embed, embedMany } from "ai"; +import { transformersJS } from "@browser-ai/transformers-js"; + +// Single embedding +const { embedding } = await embed({ + model: transformersJS.embedding("Supabase/gte-small"), + value: "Hello, world!", +}); + +// Multiple embeddings +const { embeddings } = await embedMany({ + model: transformersJS.embedding("Supabase/gte-small"), + values: ["Hello", "World", "AI"], +}); +``` + +## Audio transcription + +```js +import { experimental_transcribe as transcribe } from "ai"; +import { transformersJS } from "@browser-ai/transformers-js"; + +const transcript = await transcribe({ + model: transformersJS.transcription("Xenova/whisper-base"), + audio: audioFile, +}); +console.log(transcript.text); +console.log(transcript.segments); // segments with timestamps +``` + +## Vision models + +```js +import { streamText } from "ai"; +import { transformersJS } from "@browser-ai/transformers-js"; + +const result = streamText({ + model: transformersJS("HuggingFaceTB/SmolVLM-256M-Instruct", { + isVisionModel: true, + device: "webgpu", + }), + messages: [ + { + role: "user", + content: [ + { type: "text", text: "Describe this image" }, + { type: "image", image: someImageBlobOrUrl }, + ], + }, + ], +}); + +for await (const chunk of result.textStream) { + console.log(chunk); +} +``` + +## Web Worker offloading + +For better performance, run model inference off the main thread with a Web Worker. + +**1. Create `worker.ts`:** + +```typescript +import { TransformersJSWorkerHandler } from "@browser-ai/transformers-js"; + +const handler = new TransformersJSWorkerHandler(); +self.onmessage = (msg: MessageEvent) => { + handler.onmessage(msg); +}; +``` + +**2. Pass the worker when creating the model:** + +```js +import { streamText } from "ai"; +import { transformersJS } from "@browser-ai/transformers-js"; + +const model = transformersJS("HuggingFaceTB/SmolLM2-360M-Instruct", { + device: "webgpu", + worker: new Worker(new URL("./worker.ts", import.meta.url), { + type: "module", + }), +}); + +const result = streamText({ + model, + messages: [{ role: "user", content: "Hello!" }], +}); +``` + +## Download progress tracking + +Models are downloaded on first use. Track progress to provide a better UX: + +```js +import { streamText } from "ai"; +import { transformersJS } from "@browser-ai/transformers-js"; + +const model = transformersJS("HuggingFaceTB/SmolLM2-360M-Instruct"); +const availability = await model.availability(); + +if (availability === "unavailable") { + console.log("Browser doesn't support Transformers.js"); +} else if (availability === "downloadable") { + await model.createSessionWithProgress(({ progress }) => { + console.log(`Download progress: ${Math.round(progress * 100)}%`); + }); +} + +// Model is ready +const result = streamText({ model, prompt: "Hello!" }); +``` + +## Tool calling + + + +For best tool calling results, use reasoning models like Qwen3 which handle multi-step reasoning well. + + + +```js +import { streamText, tool, stepCountIs } from "ai"; +import { transformersJS } from "@browser-ai/transformers-js"; +import { z } from "zod"; + +const result = await streamText({ + model: transformersJS("onnx-community/Qwen3-0.6B-ONNX"), + messages: [{ role: "user", content: "What's the weather in San Francisco?" }], + tools: { + weather: tool({ + description: "Get the weather in a location", + inputSchema: z.object({ + location: z.string().describe("The location to get the weather for"), + }), + execute: async ({ location }) => ({ + location, + temperature: 72 + Math.floor(Math.random() * 21) - 10, + }), + }), + }, + stopWhen: stepCountIs(5), +}); +``` + +Tool calling also supports [tool execution approval (`needsApproval`)](https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling#tool-execution-approval) for human-in-the-loop workflows. + +## `useChat` with custom transport + +When using the `useChat` hook, you create a [custom transport](https://ai-sdk.dev/docs/ai-sdk-ui/transport) to handle client-side inference. Here's a minimal example: + +```typescript +import { + ChatTransport, UIMessageChunk, streamText, + convertToModelMessages, ChatRequestOptions, +} from "ai"; +import { + TransformersJSLanguageModel, + TransformersUIMessage, +} from "@browser-ai/transformers-js"; + +export class TransformersChatTransport + implements ChatTransport +{ + constructor(private readonly model: TransformersJSLanguageModel) {} + + async sendMessages( + options: { + chatId: string; + messages: TransformersUIMessage[]; + abortSignal: AbortSignal | undefined; + } & { + trigger: "submit-message" | "submit-tool-result" | "regenerate-message"; + messageId: string | undefined; + } & ChatRequestOptions, + ): Promise> { + const prompt = await convertToModelMessages(options.messages); + const result = streamText({ + model: this.model, + messages: prompt, + abortSignal: options.abortSignal, + }); + return result.toUIMessageStream(); + } + + async reconnectToStream(): Promise | null> { + return null; // client-side AI doesn't support stream reconnection + } +} +``` + +Then use it in your component: + +```typescript +import { useChat } from "@ai-sdk/react"; +import { transformersJS, TransformersUIMessage } from "@browser-ai/transformers-js"; + +const model = transformersJS("HuggingFaceTB/SmolLM2-360M-Instruct", { + device: "webgpu", + worker: new Worker(new URL("./worker.ts", import.meta.url), { type: "module" }), +}); + +const { sendMessage, messages, stop } = useChat({ + transport: new TransformersChatTransport(model), +}); +``` + +## Browser compatibility fallback + +If the device doesn't support in-browser inference, you can fall back to a server-side model: + +```typescript +import { + transformersJS, TransformersUIMessage, + doesBrowserSupportTransformersJS, +} from "@browser-ai/transformers-js"; + +const { sendMessage, messages, stop } = useChat({ + transport: doesBrowserSupportTransformersJS() + ? new TransformersChatTransport(model) + : new DefaultChatTransport({ api: "/api/chat" }), +}); +``` + +## Further reading + +- [Building a Next.js AI Chatbot](../tutorials/next-ai-sdk) — a step-by-step tutorial building a full chatbot with tool calling +- [`@browser-ai/transformers-js` documentation](https://www.browser-ai.dev/docs/ai-sdk-v6/transformers-js) +- [Vercel AI SDK documentation](https://ai-sdk.dev/) diff --git a/packages/transformers/docs/source/tutorials/next-ai-sdk.md b/packages/transformers/docs/source/tutorials/next-ai-sdk.md new file mode 100644 index 000000000..c91dc5ed5 --- /dev/null +++ b/packages/transformers/docs/source/tutorials/next-ai-sdk.md @@ -0,0 +1,436 @@ +# Building a Next.js AI Chatbot with Vercel AI SDK + +In this tutorial, we'll build an in-browser AI chatbot using Next.js, Transformers.js, and the Vercel AI SDK v6. The chatbot runs entirely client-side with WebGPU acceleration — and supports tool calling with human approval. + +Useful links: +- [Source code](https://github.com/huggingface/transformers.js/tree/main/next-vercel-ai-sdk-v6-tool-calling) +- [`@browser-ai/transformers-js` docs](https://www.browser-ai.dev/docs/ai-sdk-v6/transformers-js) +- [Vercel AI SDK docs](https://ai-sdk.dev/) + +## Prerequisites + +- [Node.js](https://nodejs.org/en/) version 18+ +- [npm](https://www.npmjs.com/) version 9+ +- A browser with WebGPU support (Chrome 113+, Edge 113+, or Firefox/Safari with flags enabled) + +## Step 1: Create the project + +Create a new Next.js application: + +```bash +npx create-next-app@latest next-ai-chatbot +cd next-ai-chatbot +``` + +Install the AI and Transformers.js dependencies: + +```bash +npm install ai @ai-sdk/react @browser-ai/transformers-js @huggingface/transformers zod +``` + +## Step 2: Configure Next.js for browser inference + +Transformers.js uses ONNX Runtime under the hood. We need to tell Next.js to exclude the Node.js-specific packages when bundling for the browser. Update `next.config.ts`: + +```typescript +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + output: "export", // optional: export as a static site + webpack: (config) => { + config.resolve.alias = { + ...config.resolve.alias, + sharp$: false, + "onnxruntime-node$": false, + }; + return config; + }, +}; + +export default nextConfig; +``` + +## Step 3: Create the Web Worker + +Running model inference on the main thread would block the UI. The `@browser-ai/transformers-js` package provides a ready-made worker handler that handles all the complexity for you. + +Create `src/app/worker.ts`: + +```typescript +import { TransformersJSWorkerHandler } from "@browser-ai/transformers-js"; + +const handler = new TransformersJSWorkerHandler(); +self.onmessage = (msg: MessageEvent) => { + handler.onmessage(msg); +}; +``` + +That's it — the handler takes care of model loading, inference, streaming, and communication with the main thread. + +## Step 4: Define the model configuration + +Create `src/app/models.ts` to define which models are available. These are ONNX-format models from Hugging Face: + +```typescript +export interface ModelConfig { + id: string; + name: string; + device?: string; + dtype?: string; + supportsWorker?: boolean; +} + +export const MODELS: ModelConfig[] = [ + { + id: "onnx-community/Qwen3-0.6B-ONNX", + name: "Qwen3 0.6B", + device: "webgpu", + dtype: "q4f16", + supportsWorker: true, + }, + { + id: "onnx-community/granite-4.0-350m-ONNX-web", + name: "Granite 4.0 350M", + device: "webgpu", + dtype: "fp16", + supportsWorker: false, + }, +]; +``` + + + +For tool calling, use reasoning models like Qwen3 which handle multi-step reasoning well, or fine-tuned model specifically for tool-calling capabilities. The `supportsWorker` flag controls whether the model is loaded in a Web Worker for better performance. + + + +## Step 5: Define tools + +Create `src/app/tools.ts` with tools the model can call. Each tool uses [Zod](https://zod.dev/) for input validation: + +```typescript +import { tool } from "ai"; +import z from "zod"; + +export const createTools = () => ({ + getCurrentTime: tool({ + description: "Get the current date and time.", + inputSchema: z.object({}), + execute: async () => { + const now = new Date(); + return { + timestamp: now.toISOString(), + date: now.toLocaleDateString("en-US", { + weekday: "long", year: "numeric", month: "long", day: "numeric", + }), + time: now.toLocaleTimeString("en-US", { + hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: true, + }), + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + }; + }, + }), + randomNumber: tool({ + description: "Generate a random integer between min and max (inclusive).", + inputSchema: z.object({ + min: z.number().describe("The minimum value (inclusive)"), + max: z.number().describe("The maximum value (inclusive)"), + }), + execute: async ({ min, max }) => { + return Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min) + 1)) + Math.ceil(min); + }, + }), + getLocation: tool({ + description: "Get the user's current geographic location.", + inputSchema: z.object({}), + needsApproval: true, // requires user confirmation before executing + execute: async () => { + return new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition( + (pos) => resolve({ + latitude: pos.coords.latitude, + longitude: pos.coords.longitude, + }), + (err) => reject(err.message), + ); + }); + }, + }), +}); +``` + +The `getLocation` tool uses `needsApproval: true`, which means the AI SDK will pause execution and wait for the user to approve or reject the tool call before running it. + +## Step 6: Create the chat transport + +The Vercel AI SDK's `useChat` hook needs a [transport](https://ai-sdk.dev/docs/ai-sdk-ui/transport) that handles communication with the model. For client-side inference, we implement a custom `ChatTransport`. + +Create `src/app/chat-transport.ts`: + +```typescript +import { + ChatTransport, UIMessageChunk, streamText, + convertToModelMessages, ChatRequestOptions, + createUIMessageStream, stepCountIs, +} from "ai"; +import { + TransformersJSLanguageModel, + TransformersUIMessage, + transformersJS, +} from "@browser-ai/transformers-js"; +import { MODELS } from "./models"; +import { createTools } from "./tools"; + +export class TransformersChatTransport + implements ChatTransport +{ + private model: TransformersJSLanguageModel; + private tools: ReturnType; + + constructor() { + const config = MODELS[0]; + this.model = transformersJS(config.id, { + device: config.device, + dtype: config.dtype, + ...(config.supportsWorker + ? { + worker: new Worker(new URL("./worker.ts", import.meta.url), { + type: "module", + }), + } + : {}), + }); + this.tools = createTools(); + } + + async sendMessages( + options: { + chatId: string; + messages: TransformersUIMessage[]; + abortSignal: AbortSignal | undefined; + } & { + trigger: "submit-message" | "submit-tool-result" | "regenerate-message"; + messageId: string | undefined; + } & ChatRequestOptions, + ): Promise> { + const { messages, abortSignal } = options; + const prompt = await convertToModelMessages(messages); + + return createUIMessageStream({ + execute: async ({ writer }) => { + // Track download progress if the model hasn't been downloaded yet + let downloadProgressId: string | undefined; + const availability = await this.model.availability(); + + if (availability !== "available") { + await this.model.createSessionWithProgress( + (progress: { progress: number }) => { + const percent = Math.round(progress.progress * 100); + + if (progress.progress >= 1) { + if (downloadProgressId) { + writer.write({ + type: "data-modelDownloadProgress", + id: downloadProgressId, + data: { + status: "complete", progress: 100, + message: "Model ready!", + }, + }); + } + return; + } + + if (!downloadProgressId) { + downloadProgressId = `download-${Date.now()}`; + } + + writer.write({ + type: "data-modelDownloadProgress", + id: downloadProgressId, + data: { + status: "downloading", progress: percent, + message: `Downloading model... ${percent}%`, + }, + }); + }, + ); + } + + const result = streamText({ + model: this.model, + tools: this.tools, + stopWhen: stepCountIs(5), + messages: prompt, + abortSignal, + }); + + writer.merge(result.toUIMessageStream({ sendStart: false })); + }, + }); + } + + async reconnectToStream(): Promise | null> { + return null; + } +} +``` + +Key parts of the transport: +- **Availability check**: Determines if the model needs downloading before inference. +- **Progress streaming**: Sends download progress as custom data parts (`data-modelDownloadProgress`) that the UI can render as a progress bar. +- **Tool support**: Passes the tools to `streamText()` so the model can call them. +- **Step limiting**: `stopWhen: stepCountIs(5)` prevents infinite tool-calling loops. + +## Step 7: Build the chat UI + +Now wire everything together in your page component. Create `src/app/page.tsx`: + +```tsx +"use client"; + +import { useState } from "react"; +import { useChat } from "@ai-sdk/react"; +import { TransformersUIMessage } from "@browser-ai/transformers-js"; +import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai"; +import { TransformersChatTransport } from "./chat-transport"; + +export default function ChatPage() { + const [input, setInput] = useState(""); + + const { + messages, + sendMessage, + status, + stop, + addToolApprovalResponse, + } = useChat({ + transport: new TransformersChatTransport(), + experimental_throttle: 75, + // Automatically resumes after tool approval responses are submitted + sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses, + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (input.trim() && status === "ready") { + sendMessage({ text: input }); + setInput(""); + } + }; + + return ( +
+

AI Chatbot

+ +
+ {messages.map((message) => ( +
+ {message.role === "user" ? "You" : "Assistant"}: + {message.parts.map((part, i) => { + switch (part.type) { + case "text": + return

{part.text}

; + + case "data-modelDownloadProgress": + if (!part.data.message) return null; + return ( +
+

{part.data.message}

+ {part.data.status === "downloading" && ( + + )} +
+ ); + + default: + // Handle tool parts + if (part.type.startsWith("tool-") && "state" in part) { + if ( + part.state === "approval-requested" && + "approval" in part + ) { + return ( +
+

Tool {part.type.replace("tool-", "")} wants to run.

+ + +
+ ); + } + if ("output" in part && part.output) { + return ( +
+                          {JSON.stringify(part.output, null, 2)}
+                        
+ ); + } + } + return null; + } + })} +
+ ))} +
+ + {status === "submitted" &&

Thinking...

} + +
+ setInput(e.target.value)} + placeholder="Ask something..." + style={{ width: "100%", padding: 8 }} + /> +
+ {status === "streaming" ? ( + + ) : ( + + )} +
+
+
+ ); +} +``` + +The component renders message parts based on their `type`: +- `text` — standard text output from the model. +- `data-modelDownloadProgress` — custom data parts sent by the transport during model download. +- `tool-*` — tool call parts with states like `approval-requested`, `output-available`, etc. + +The `sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses` option tells `useChat` to automatically resume generation after the user responds to a tool approval request. + +## Step 8: Run the application + +Start the development server: + +```bash +npm run dev +``` + +Open your browser and navigate to the URL shown in the terminal. The first time you send a message, the model will be downloaded and cached in the browser. Subsequent visits will load the cached model. + +Try prompts like: +- "What time is it?" +- "Generate a random number between 1 and 100" +- "Where am I located?" (this will trigger a tool approval prompt) + +## Next steps + +- Add more models and a model selector — see the [full example source](https://github.com/huggingface/transformers.js/tree/main/next-vercel-ai-sdk-v6-tool-calling) for a multi-model implementation with Zustand state management. +- Add a browser compatibility check with `doesBrowserSupportTransformersJS()` and fall back to a server-side route if WebGPU is unavailable. +- Explore the [Vercel AI SDK agents documentation](https://ai-sdk.dev/docs/agents/overview) for more complex agent patterns. +- See the [Vercel AI SDK guide](../guides/vercel-ai-sdk) for a reference of all supported features (embeddings, vision, transcription, etc.). From 9fd4d2f62b0c4d06b620bc50c7e3d7b308d156a3 Mon Sep 17 00:00:00 2001 From: jakobhoeg Date: Wed, 25 Feb 2026 20:21:11 +0100 Subject: [PATCH 2/8] refactor: wording --- packages/transformers/docs/source/guides/vercel-ai-sdk.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/transformers/docs/source/guides/vercel-ai-sdk.md b/packages/transformers/docs/source/guides/vercel-ai-sdk.md index 15335d8fb..9f5d54154 100644 --- a/packages/transformers/docs/source/guides/vercel-ai-sdk.md +++ b/packages/transformers/docs/source/guides/vercel-ai-sdk.md @@ -6,7 +6,8 @@ This guide covers the core concepts and API patterns. For a full step-by-step pr ## Why use the Vercel AI SDK with Transformers.js? -Building functional and production-ready applications using Hugging Face models in the browser with just `@huggingface/transformers` requires significant boilerplate: Web Worker setup, message passing, progress tracking, streaming, interrupt handling, error states, and state management. The `@browser-ai/transformers-js` provider abstracts all of this away, letting you use the same `streamText`, `generateText`, and `useChat` APIs you'd use with any other AI SDK provider. Read more about this [here](https://www.browser-ai.dev/docs/ai-sdk-v6/transformers-js/why). +The `@browser-ai/transformers-js` provider builds on top of `@huggingface/transformers` to give you a standard AI SDK interface — handling Web Worker setup, message passing, progress tracking, streaming, interrupt handling, and state management, so you can use the same `streamText`, `generateText`, and `useChat` APIs you'd use with any other AI SDK provider. +Read more about this [here](https://www.browser-ai.dev/docs/ai-sdk-v6/transformers-js/why). ## Installation From df67943ac13d8c3758cf18ed6206f701fd460a27 Mon Sep 17 00:00:00 2001 From: jakobhoeg Date: Thu, 26 Feb 2026 19:43:40 +0100 Subject: [PATCH 3/8] fix: add doc sites to `table of contents` --- packages/transformers/docs/source/_toctree.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/transformers/docs/source/_toctree.yml b/packages/transformers/docs/source/_toctree.yml index bfbff8134..3041e0b42 100644 --- a/packages/transformers/docs/source/_toctree.yml +++ b/packages/transformers/docs/source/_toctree.yml @@ -21,6 +21,8 @@ title: Building an Electron Application - local: tutorials/node title: Server-side Inference in Node.js + - local: tutorials/next-ai-sdk + title: Building a Next.js AI Chatbot with Vercel AI SDK title: Tutorials - sections: - local: guides/webgpu @@ -31,6 +33,8 @@ title: Accessing Private/Gated Models - local: guides/node-audio-processing title: Server-side Audio Processing + - local: guides/vercel-ai-sdk + title: Using Transformers.js with the Vercel AI SDK title: Developer Guides - sections: - local: api/transformers From 2007eda038d0794aba24da4c9c5788494c27d13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20Hoeg=20M=C3=B8rk?= <114422072+jakobhoeg@users.noreply.github.com> Date: Thu, 26 Feb 2026 19:50:06 +0100 Subject: [PATCH 4/8] refactor: wording Co-authored-by: Nico Martin --- packages/transformers/docs/source/tutorials/next-ai-sdk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/transformers/docs/source/tutorials/next-ai-sdk.md b/packages/transformers/docs/source/tutorials/next-ai-sdk.md index c91dc5ed5..a4090dced 100644 --- a/packages/transformers/docs/source/tutorials/next-ai-sdk.md +++ b/packages/transformers/docs/source/tutorials/next-ai-sdk.md @@ -30,7 +30,7 @@ npm install ai @ai-sdk/react @browser-ai/transformers-js @huggingface/transforme ## Step 2: Configure Next.js for browser inference -Transformers.js uses ONNX Runtime under the hood. We need to tell Next.js to exclude the Node.js-specific packages when bundling for the browser. Update `next.config.ts`: +Transformers.js uses ONNX Runtime under the hood for both browser and server-side (Node.js) inference. In our case we only need the browser runtime so we can tell Next.js to exclude the Node.js-specific packages when bundling for the browser. Update `next.config.ts` ```typescript import type { NextConfig } from "next"; From 8c4a4f528d79b05c615deab41f7d610e9d74ec16 Mon Sep 17 00:00:00 2001 From: jakobhoeg Date: Thu, 26 Feb 2026 21:09:46 +0100 Subject: [PATCH 5/8] fix: broken link --- packages/transformers/docs/source/tutorials/next-ai-sdk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/transformers/docs/source/tutorials/next-ai-sdk.md b/packages/transformers/docs/source/tutorials/next-ai-sdk.md index a4090dced..5838a50a0 100644 --- a/packages/transformers/docs/source/tutorials/next-ai-sdk.md +++ b/packages/transformers/docs/source/tutorials/next-ai-sdk.md @@ -3,7 +3,7 @@ In this tutorial, we'll build an in-browser AI chatbot using Next.js, Transformers.js, and the Vercel AI SDK v6. The chatbot runs entirely client-side with WebGPU acceleration — and supports tool calling with human approval. Useful links: -- [Source code](https://github.com/huggingface/transformers.js/tree/main/next-vercel-ai-sdk-v6-tool-calling) +- [Source code](https://github.com/huggingface/transformers.js-examples/tree/main/next-vercel-ai-sdk-v6-tool-calling) - [`@browser-ai/transformers-js` docs](https://www.browser-ai.dev/docs/ai-sdk-v6/transformers-js) - [Vercel AI SDK docs](https://ai-sdk.dev/) From 04671f8ba4c49412f3ba335e35398994d12615be Mon Sep 17 00:00:00 2001 From: jakobhoeg Date: Thu, 26 Feb 2026 21:14:07 +0100 Subject: [PATCH 6/8] fix: link --- packages/transformers/docs/source/tutorials/next-ai-sdk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/transformers/docs/source/tutorials/next-ai-sdk.md b/packages/transformers/docs/source/tutorials/next-ai-sdk.md index 5838a50a0..f8ddbbca7 100644 --- a/packages/transformers/docs/source/tutorials/next-ai-sdk.md +++ b/packages/transformers/docs/source/tutorials/next-ai-sdk.md @@ -430,7 +430,7 @@ Try prompts like: ## Next steps -- Add more models and a model selector — see the [full example source](https://github.com/huggingface/transformers.js/tree/main/next-vercel-ai-sdk-v6-tool-calling) for a multi-model implementation with Zustand state management. +- Add more models and a model selector — see the [full example source](https://github.com/huggingface/transformers.js-examples/tree/main/next-vercel-ai-sdk-v6-tool-calling) for a multi-model implementation with Zustand state management. - Add a browser compatibility check with `doesBrowserSupportTransformersJS()` and fall back to a server-side route if WebGPU is unavailable. - Explore the [Vercel AI SDK agents documentation](https://ai-sdk.dev/docs/agents/overview) for more complex agent patterns. - See the [Vercel AI SDK guide](../guides/vercel-ai-sdk) for a reference of all supported features (embeddings, vision, transcription, etc.). From 454cdf4826e40d9f79c504b11d33e052ccc287af Mon Sep 17 00:00:00 2001 From: jakobhoeg Date: Thu, 26 Feb 2026 21:30:51 +0100 Subject: [PATCH 7/8] refactor: update doc --- .../docs/source/tutorials/next-ai-sdk.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/transformers/docs/source/tutorials/next-ai-sdk.md b/packages/transformers/docs/source/tutorials/next-ai-sdk.md index f8ddbbca7..19c224dc3 100644 --- a/packages/transformers/docs/source/tutorials/next-ai-sdk.md +++ b/packages/transformers/docs/source/tutorials/next-ai-sdk.md @@ -72,11 +72,11 @@ That's it — the handler takes care of model loading, inference, streaming, Create `src/app/models.ts` to define which models are available. These are ONNX-format models from Hugging Face: ```typescript -export interface ModelConfig { +import { WorkerLoadOptions } from "@browser-ai/transformers-js"; + +export interface ModelConfig extends Omit { id: string; name: string; - device?: string; - dtype?: string; supportsWorker?: boolean; } @@ -93,7 +93,7 @@ export const MODELS: ModelConfig[] = [ name: "Granite 4.0 350M", device: "webgpu", dtype: "fp16", - supportsWorker: false, + supportsWorker: true, }, ]; ``` @@ -224,10 +224,10 @@ export class TransformersChatTransport if (availability !== "available") { await this.model.createSessionWithProgress( - (progress: { progress: number }) => { - const percent = Math.round(progress.progress * 100); + (progress: number) => { + const percent = Math.round(progress * 100); - if (progress.progress >= 1) { + if (progress >= 1) { if (downloadProgressId) { writer.write({ type: "data-modelDownloadProgress", From 25a14ae251cacd2d2e2512b2b13fee9e04f78899 Mon Sep 17 00:00:00 2001 From: jakobhoeg Date: Thu, 26 Feb 2026 21:31:15 +0100 Subject: [PATCH 8/8] fix: Nextjs 16+ turbopack --- packages/transformers/docs/source/tutorials/next-ai-sdk.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/transformers/docs/source/tutorials/next-ai-sdk.md b/packages/transformers/docs/source/tutorials/next-ai-sdk.md index 19c224dc3..a9f69ad17 100644 --- a/packages/transformers/docs/source/tutorials/next-ai-sdk.md +++ b/packages/transformers/docs/source/tutorials/next-ai-sdk.md @@ -37,6 +37,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: "export", // optional: export as a static site + turbopack: {}, webpack: (config) => { config.resolve.alias = { ...config.resolve.alias,