@localmode/chrome-ai
v2.3.0
Published
Chrome AI provider for @localmode - on-device inference via Chrome's built-in Gemini Nano
Maintainers
Readme
@localmode/chrome-ai
On-device AI inference via Chrome's built-in Gemini Nano model. Your application ships and fetches no model files — Chrome supplies the model. Part of the LocalMode ecosystem.
Features
- Your app ships no model files — Chrome supplies the model and downloads it once, browser-wide, on first use (opt in with
allowDownload, track it withonProgress) - Zero bundle size impact — browser-native APIs
- No per-call model loading once the on-device model is available
- Implements
SummarizationModel,TranslationModel,LanguageDetectionModel, andLanguageModelfrom@localmode/core - Automatic fallback support — pair with
@localmode/transformers,@localmode/webllm,@localmode/wllama, or@localmode/litertfor non-Chrome browsers
Requirements
- Summarizer, Translator & Language Detector APIs: Chrome 138+ on desktop (Windows 10+, macOS 13+, Linux, or ChromeOS on Chromebook Plus)
- Prompt API (
languageModel()): Chrome 148+ on desktop — Chrome 138 shipped the Prompt API for extensions only; it reached web pages in 148 - 22 GB free disk space on the volume containing your Chrome profile (for the Gemini Nano model)
- Hardware: GPU with >4 GB VRAM, or CPU with 16 GB+ RAM and 4+ cores
- Not available on mobile (Android/iOS) or in Incognito mode
- Non-Chrome browsers need a fallback provider (e.g.,
@localmode/transformers)
Enabling Chrome AI
On a supported Chrome stable build, no flags are required — these APIs ship on by default. What you need is for Chrome to have fetched the on-device model, which happens once, browser-wide.
Verify in the DevTools console:
'Summarizer' in self; // Summarizer — Chrome 138+
'LanguageDetector' in self; // Language Detector — Chrome 138+
'LanguageModel' in self; // Prompt API — Chrome 148+
// 'available' | 'downloadable' | 'downloading' | 'unavailable'
await Summarizer.availability();If it reports 'downloadable', the model is not on disk yet. This package refuses to start that
download implicitly; pass allowDownload: true and call from a user activation (a click), which
Chrome requires to begin the ~1.5 GB fetch.
Installation
pnpm add @localmode/chrome-ai @localmode/coreQuick Start
import { detectLanguage, generateText, summarize, translate } from '@localmode/core';
import { chromeAI } from '@localmode/chrome-ai';
// Summarize text (instant, no download)
const { summary } = await summarize({
model: chromeAI.summarizer(),
text: 'Long article text...',
});
// Translate text
const { translation } = await translate({
model: chromeAI.translator({ targetLanguage: 'de' }),
text: 'Hello, world!',
});
// Detect the language of a text
const { languages } = await detectLanguage({
model: chromeAI.languageDetector(),
text: 'Hallo und herzlich willkommen!',
});
// Generate text with Gemini Nano via the Prompt API
const { text } = await generateText({
model: chromeAI.languageModel({ systemPrompt: 'You are concise.' }),
prompt: 'Explain TLS in one sentence.',
});Fallback Pattern
import { summarize } from '@localmode/core';
import { chromeAI, isSummarizerAPISupported } from '@localmode/chrome-ai';
import { transformers } from '@localmode/transformers';
const model = isSummarizerAPISupported()
? chromeAI.summarizer()
: transformers.summarizer('Xenova/distilbart-cnn-6-6');
const { summary } = await summarize({ model, text: 'Long article...' });See this pattern running in a real app: the Writing Tools blocks use Chrome AI with automatic Transformers.js fallback — Write (Prompt API), Translate (Translator API), and Summarize (Summarizer API) each resolve a Chrome AI capability when available and fall back to Transformers.js otherwise, the canonical end-to-end example of the fallback pattern above. (The fourth block, Complete, is Transformers.js-only — there is no Chrome AI fill-mask to fall back from.)
API
chromeAI.summarizer(settings?)
Creates a SummarizationModel using Chrome's Summarizer API.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| type | 'key-points' \| 'tldr' \| 'teaser' \| 'headline' | 'tldr' | Summary type. Chrome's enum spells it tldr; 'tl;dr' throws a TypeError. |
| format | 'markdown' \| 'plain-text' | 'plain-text' | Output format |
| length | 'short' \| 'medium' \| 'long' | 'medium' | Summary length |
| sharedContext | string | — | Context shared across calls |
| outputLanguage | string | unset | Language of the summary (BCP 47; Chrome 156 accepts en, es, ja, de, fr). Chrome logs a console warning when unset. |
| expectedInputLanguages | string[] | unset | Languages the input will be in |
| expectedContextLanguages | string[] | unset | Languages sharedContext will be in |
| allowDownload | boolean | false | Let Chrome download the model when availability() is downloadable. Requires a user activation. |
| onProgress | (p: { loaded: number; total: number }) => void | — | Model download progress as a fraction: loaded runs 0 to 1, total is 1 (not bytes) |
chromeAI.translator(settings?)
Creates a TranslationModel using Chrome's Translator API.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| sourceLanguage | string | 'en' | Source language (BCP 47) |
| targetLanguage | string | 'es' | Target language (BCP 47) |
| allowDownload | boolean | false | Let Chrome download the language pack for this directed pair. Requires a user activation. |
| onProgress | (p: { loaded: number; total: number }) => void | — | Language-pack download progress as a fraction: loaded runs 0 to 1, total is 1 (not bytes) |
Streaming, input quota and model info (summarizer and translator)
chromeAI.summarizer() and chromeAI.translator() return ChromeAISummarizer / ChromeAITranslator, which add:
| Method | Description |
|--------|-------------|
| doStreamSummarize({ text, context?, maxLength?, abortSignal?, providerOptions? }) / doStreamTranslate({ text, sourceLanguage?, targetLanguage?, abortSignal? }) | Async iterable over Chrome's summarizeStreaming() / translateStreaming(): { text, done: false, timestamp } per chunk (performance.now() on arrival), then { done: true, timing: { requestedAt, firstChunkAt, endedAt, chunkCount, chars }, usage } |
| measureInputUsage(text, options?) | Chrome's measureInputUsage() for the session (translator: per pair) |
| getInputQuota(options?) | The session's inputQuota, or null |
| getModelInfo(options?) | availabilityAtCreate, requestedOptions, sessionOptions (as the session reports them), inputQuota, browser (getChromeBuildInfo()), modelVersion: null |
const model = chromeAI.summarizer({ type: 'tldr' });
for await (const chunk of model.doStreamSummarize({ text: article })) {
if (!chunk.done) render(chunk.text);
else console.log(chunk.timing!.firstChunkAt! - chunk.timing!.requestedAt);
}getChromeBuildInfo() returns userAgent, brands, fullVersionList, platform, platformVersion and architecture from navigator.userAgentData (null where absent). Chrome does not expose the on-device model version to web pages; read it from chrome://components.
chromeAI.languageDetector(settings?)
Creates a LanguageDetectionModel using Chrome's Language Detector API. Returns BCP 47 codes with confidences, highest first; Chrome's und (undetermined) entry is dropped. Construction never throws; gate with isLanguageDetectorSupported().
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| expectedInputLanguages | string[] | — | Languages the input is expected to be in (BCP 47), forwarded to Chrome |
| topK | number | all | Keep the topK most confident languages. Also settable per call via providerOptions.chromeAI.topK. |
| allowDownload | boolean | false | Let Chrome download the detection model. Also settable per call via providerOptions.chromeAI.allowDownload. Requires a user activation. |
| onProgress | (p: { loaded: number; total: number }) => void | — | Model download progress as a fraction: loaded runs 0 to 1, total is 1 (not bytes) |
Errors are TranslationErrors with a code: chrome-ai-not-supported, chrome-ai-model-not-available, chrome-ai-download-required, chrome-ai-permissions-denied, chrome-ai-quota-exceeded, or chrome-ai-detection-failed; all but the last carry retryable: false, so detectLanguage() fails at once instead of retrying. ChromeAILanguageDetector also has warmUp(), isReady(), and destroy().
chromeAI.languageModel(settings?)
Creates a LanguageModel using Chrome's Prompt API (Gemini Nano). Supports generateText(), streamText(), generateObject(), and the model-warmup protocol via warmUp() / isReady().
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| systemPrompt | string | — | Prepended to every session as initialPrompts[0] |
| temperature | number | Chrome default | Sampling temperature (0–1) |
| topK | number | Chrome default | Top-K sampling cutoff |
| contextLength | number | 6144 | Soft documentation value for model.contextLength |
| onProgress | (p: { loaded: number; total: number }) => void | — | Forwarded to monitor for Gemini Nano download progress as a fraction: loaded runs 0 to 1, total is 1 (not bytes) |
| allowDownload | boolean | false | Let Chrome download Gemini Nano. Also settable per call via providerOptions.chromeAI.allowDownload. Requires a user activation. |
import { generateText } from '@localmode/core';
import { chromeAI } from '@localmode/chrome-ai';
const { text } = await generateText({
model: chromeAI.languageModel({ systemPrompt: 'You are concise.' }),
prompt: 'Explain quantum tunnelling in one sentence.',
});Lifecycle Methods
ChromeAILanguageModel exposes lifecycle methods for fine-grained control:
| Method | Returns | Description |
|--------|---------|-------------|
| warmUp() | Promise<void> | Pre-initialize the Gemini Nano session so the next doGenerate() / doStream() has zero creation latency. Pairs with useModelWarmup() from @localmode/react. |
| isReady() | boolean | true once a session is cached on this instance; false otherwise. |
| destroy() | void | Release the cached session and free resources. Idempotent. Subsequent calls recreate a fresh session. |
import { ChromeAILanguageModel } from '@localmode/chrome-ai';
const model = new ChromeAILanguageModel({
systemPrompt: 'You are concise.',
temperature: 0.3,
});
await model.warmUp();
console.log(model.isReady()); // true
// ... use model with generateText() / streamText() ...
model.destroy(); // release resourcesSee the Language Model docs for streaming, structured output, fallback chains, and the full error reference.
Feature Detection
import {
getChromeBuildInfo,
isChromeAISupported,
isLanguageDetectorSupported,
isPromptAPISupported,
isSummarizerAPISupported,
isTranslatorAPISupported,
} from '@localmode/chrome-ai';
if (isChromeAISupported()) { /* Chrome AI available */ }
if (isLanguageDetectorSupported()) { /* Language Detector API available */ }
if (isPromptAPISupported()) { /* Prompt API (LanguageModel) available */ }
if (isSummarizerAPISupported()) { /* Summarizer API available */ }
if (isTranslatorAPISupported()) { /* Translator API available */ }Exported Types
The package exports TypeScript types for Chrome's built-in AI APIs:
| Type | Description |
|------|-------------|
| ChromeAIProvider | Provider interface with summarizer(), translator(), languageDetector(), languageModel() |
| ChromeAIProviderSettings | Provider-level configuration |
| ChromeAILanguageModelSettings | Settings for languageModel() (systemPrompt, temperature, topK, etc.) |
| ChromeAISummarizerSettings | Settings for summarizer() (type, format, length) |
| ChromeAITranslatorSettings | Settings for translator() (sourceLanguage, targetLanguage) |
| ChromeAILanguageDetectorSettings | Settings for languageDetector() (expectedInputLanguages, topK, allowDownload) |
| AILanguageModel | Chrome Prompt API session interface |
| ChromeAIAvailability | On-device model state reported by every factory's availability(): 'available' \| 'downloadable' \| 'downloading' \| 'unavailable' |
| AILanguageModelAvailability | Alias of ChromeAIAvailability, kept for backward compatibility |
| AILanguageModelCreateOptions | Options for LanguageModel.create() |
| AILanguageModelFactory | Chrome Prompt API factory (window.LanguageModel) |
| AILanguageModelPromptOptions | Per-call options for prompt() / promptStreaming() |
| AISummarizer | Chrome Summarizer API session interface |
| AISummarizerFactory | Chrome Summarizer API factory |
| AISummarizerCapabilities | Summarizer capability detection |
| AISummarizerCreateOptions | Options for Summarizer.create() |
| AITranslator | Chrome Translator API session interface |
| AITranslatorFactory | Chrome Translator API factory |
| AITranslatorCapabilities | Translator capability detection |
| AITranslatorCreateOptions | Options for Translator.create() |
| AILanguageDetector | Chrome Language Detector API session interface |
| AILanguageDetectorFactory | Chrome Language Detector API factory (self.LanguageDetector) |
| AILanguageDetectorCreateOptions | Options for LanguageDetector.create() |
| AILanguageDetectionResult | One { detectedLanguage, confidence } candidate from detect() |
| ChromeAIStreamChunk, ChromeAIStreamTiming | Items and timing from doStreamSummarize() / doStreamTranslate() |
| ChromeAIStreamSummarizeOptions, ChromeAIStreamTranslateOptions | Options for the streaming methods |
| ChromeAIModelInfo, ChromeAIAvailabilityAtCreate, ChromeAISummarizerInfoOptions, ChromeAITranslatorInfoOptions | Result of getModelInfo() |
| ChromeBuildInfo, ChromeBrandVersion | Result of getChromeBuildInfo() |
Implementation Classes
The implementation classes are exported for direct instantiation or advanced wiring:
| Class | Implements |
|-------|------------|
| ChromeAILanguageModel | LanguageModel from @localmode/core |
| ChromeAISummarizer | SummarizationModel from @localmode/core |
| ChromeAITranslator | TranslationModel from @localmode/core |
| ChromeAILanguageDetector | LanguageDetectionModel from @localmode/core |
import { ChromeAILanguageModel } from '@localmode/chrome-ai';
// Direct instantiation (bypasses provider factory)
const model = new ChromeAILanguageModel({
systemPrompt: 'You are helpful.',
topK: 40,
});Acknowledgments
This package is built on Chrome Built-in AI by Google — on-device AI APIs powered by Gemini Nano, enabling inference directly in Chrome with no model files shipped by your app.
License
MIT
