naively
v0.2.2
Published
A minimal TypeScript wrapper around [Chrome Built-in AI APIs](https://developer.chrome.com/docs/ai/built-in-apis).
Readme
naively
A minimal TypeScript wrapper around Chrome Built-in AI APIs.
Status
Chrome Built-in AI APIs are actively shipping. As of Chrome 138 (stable):
| API | Status | Supported | |-----|--------|-----------| | Summarizer | ✅ Stable | ✅ | | Translator | ✅ Stable | ✅ | | Language Detector | ✅ Stable | ✅ | | Writer / Rewriter | 🧪 Developer Trial | Soon | | Prompt API (web) | 🔬 Origin Trial | Soon |
These APIs run a local Gemini Nano model on the user's device — no API key or network request required after the initial model download.
Requirements
- Browser: Chrome 138+ (desktop only — not supported on Android or iOS)
- OS: Windows 10+, macOS 13+, Linux, or ChromeOS (Chromebook Plus)
- Storage: ~22 GB free disk space (for the model)
- Hardware: GPU with 4 GB+ VRAM, or CPU with 16 GB RAM and 4+ cores
Installation
pnpm add naively
# or
npm install naivelyUsage
Check support
import { getAiSupport, isSummarizerSupported, isTranslatorSupported, isLanguageDetectorSupported } from 'naively'
// Quick boolean checks
isSummarizerSupported() // true | false
isTranslatorSupported() // true | false
isLanguageDetectorSupported() // true | false
// Full support details
const support = await getAiSupport()
// {
// summarizer: { supported: true, availability: 'readily' | 'downloadable' | 'unavailable' | 'unsupported' }
// translator: { supported: true }
// languageDetector: { supported: true, availability: 'readily' | 'downloadable' | 'unavailable' | 'unsupported' }
// }Translator availability is language-pair specific —
naivelychecks it internally when you calltranslate().
Summarizer availability values:
| Value | Meaning |
|-------|---------|
| readily | Model is downloaded and ready |
| downloadable | Supported, but model needs to download first |
| unavailable | Hardware/OS doesn't meet requirements |
| unsupported | API not present in this browser |
Summarize text
import { summarize } from 'naively'
const result = await summarize(articleText)
if (result.ok) {
console.log(result.data)
} else {
console.error(result.error?.message)
}With options:
const result = await summarize(articleText, {
type: 'key-points', // 'tl;dr' | 'key-points' | 'teaser' | 'headline'
length: 'medium', // 'short' | 'medium' | 'long'
format: 'plain-text', // 'plain-text' | 'markdown'
})Assign expected languages
Chrome downloads language packs on demand, so declaring the languages you expect up front lets it prepare the right ones before summarizing. All values are BCP 47 language codes — see Assign expected languages in the Chrome docs.
const result = await summarize(articleText, {
expectedInputLanguages: ['en', 'es'], // languages the input text may be in
expectedContextLanguages: ['en'], // languages sharedContext/context may be in
outputLanguage: 'es', // language the summary should be written in
})Translate text
Language codes follow the BCP 47 format (e.g. 'en', 'fr', 'es', 'ja').
import { translate } from 'naively'
const result = await translate('Hello, world!', {
sourceLanguage: 'en',
targetLanguage: 'fr',
})
if (result.ok) {
console.log(result.data) // 'Bonjour, le monde !'
} else {
console.error(result.error?.message)
}API Reference
summarize(text, options?): Promise<SummarizeResult>
| Option | Type | Default |
|--------|------|---------|
| type | 'tl;dr' \| 'key-points' \| 'teaser' \| 'headline' | 'key-points' |
| length | 'short' \| 'medium' \| 'long' | 'medium' |
| format | 'plain-text' \| 'markdown' | 'plain-text' |
| sharedContext | string | — |
| expectedInputLanguages | string[] (BCP 47) | — |
| expectedContextLanguages | string[] (BCP 47) | — |
| outputLanguage | string (BCP 47) | — |
detectLanguage(text): Promise<DetectLanguageResult>
Returns a ranked list of language candidates with confidence scores (0.0–1.0):
const result = await detectLanguage('Bonjour le monde')
if (result.ok) {
console.log(result.data)
// [
// { detectedLanguage: 'fr', confidence: 0.998 },
// { detectedLanguage: 'en', confidence: 0.001 },
// ]
}Accuracy is low for very short text or single words.
translate(text, options): Promise<TranslateResult>
| Option | Type | Required |
|--------|------|----------|
| sourceLanguage | string (BCP 47) | ✅ |
| targetLanguage | string (BCP 47) | ✅ |
getAiSupport()
Returns support and availability details for all built-in AI APIs.
isSummarizerSupported() / isTranslatorSupported(): boolean
Synchronous presence checks.
Both summarize() and translate() always return a result without throwing:
interface SummarizeResult {
ok: boolean
data?: string
error?: { code: string; message: string }
}
interface TranslateResult {
ok: boolean
data?: string
error?: { code: string; message: string }
}Edge cases handled internally: empty text, API not present, model unavailable, creation/runtime errors, and model cleanup (destroy()).
Design Notes
- No session reuse. Every call to
summarize(),summarizeStreaming(),translate(), anddetectLanguage()creates a fresh model instance and callsdestroy()on it once the call finishes. This keeps the API stateless and leak-free, but it means each call pays the model'screate()cost again — there's currently no way to hold a session open across multiple calls. If you need to process many texts back-to-back with the same options, batch them yourself and be aware of the repeated setup cost; a session-reuse API may be added if a concrete use case needs it.
Development
pnpm install
pnpm build # outputs to dist/
pnpm typecheck # tsc --noEmit
pnpm test # runs the vitest suite (jsdom-mocked browser AI APIs)Further Reading
- Chrome Built-in AI overview
- Summarizer API docs
- Translator API docs
- Language Detector API docs
- People + AI Guidebook — UX guidance for AI features
@types/dom-chromium-ai— official TypeScript types for Chrome AI APIs
