Reference version

This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

Expo Expo AI

A library for generating text, structured values, and tool calls with a system-provided on-device language model.

Android
iOS
macOS
Web

expo-ai provides access to Apple's Foundation Models framework, Gemini Nano through Google's ML Kit Prompt API, and the browser's Prompt API. It runs inference locally on the device; the package does not send prompts, images, or results to an external service. Use it to summarize text, categorize content, generate structured values, and let a model call application tools. Each top-level generation call owns an independent session and releases it when the task finishes.

Platform support

On iOS 26 and later and macOS 26 and later, the device must support Apple Intelligence, Apple Intelligence must be enabled, and the system model must be ready.

On Android, this preview uses ML Kit Prompt 1.0.0-beta4 for text generation and streaming. The device must support the Prompt API and have a ready Gemini Nano model through Android AICore. Availability depends on the device and its AICore configuration, so an Android version alone does not establish support. See Google's Prompt API device support and check availability on the actual device.

Android inference requires your app to stay in the foreground. Moving to the background rejects the active task with ERR_APP_BACKGROUND, including while JavaScript is waiting for tool approval or running a tool. Observe the supplied signal to close pending approval UI and stop cooperative tool work. Earlier successful session turns remain available. Your app decides whether to submit a new request after returning to the foreground; the package does not retry automatically.

On web, the browser must expose the modern LanguageModel API. The Chrome Prompt API is available on supported desktop devices in Chrome 148 and later. Microsoft Edge provides a developer preview with additional setup requirements. Mobile browsers and browsers without this API return unavailable. Hardware, browser settings, and model readiness also affect availability.

The table compares capabilities after a platform is eligible; it is not a device-support guarantee. Apple entries require Apple Intelligence-capable hardware with a ready model. Android entries require an app with minSdk 26 or later, a device that supports the Prompt API, and a ready Gemini Nano model.

FeatureAppleAndroid (minSdk 26 or later)Web
Text generation and streamingSupported on Apple Intelligence-capable devicesSupported on compatible devicesSupported in compatible desktop browsers
Runtime schemasNative constrained output on supported devicesLibrary validation and bounded repairsNative constrained output
Application toolsNative orchestration on supported devicesLibrary orchestration and validationLibrary orchestration and validation
Model preparationManaged by system settings on supported devicesExplicit download with optional progressExplicit preparation with optional progress
Optical character recognition (OCR) and barcode toolsiOS 26 and macOS 26 or later on supported devicesNot includedNot included
Model image understandingiOS 27 and macOS 27 on vision-capable models and supported devicesNot includedNot included

Importing the package and checking availability is safe on unsupported platforms. A successful availability check does not guarantee that a later generation will succeed. Downloadable third-party model backends are not included in this preview. A newer operating system does not automatically enable model image understanding: it also requires a compatible SDK and the selected model's vision capability.

The browser selects and manages its model. Web results identify the provider as browser-prompt-api; the model identifier remains null. maximumOutputTokens is unsupported on web and rejects if supplied. The package does not use experimental browser-native tool declarations.

Generation uses the on-device system model. Tools execute application code, so any network access or data storage in a tool belongs to your app.

Installation

Terminal
npx expo install expo-ai

If you are installing this in an existing React Native app, make sure to install expo in your project.

Create a new development build after adding the native package. Building the Apple implementation requires Xcode 26.4 or later. Your app can keep an iOS deployment target of 16.4 or later; model generation requires iOS 26 or later at runtime. Test model generation on supported hardware with Apple Intelligence enabled. This preview is not included in Expo Go and cannot run in Snack.

Model image understanding requires a build with Xcode 27 or later. Builds made with Xcode 26.4 or later retain text generation and image tools when running on newer operating systems.

Android build configuration

The Android backend requires a minimum SDK version of 26 and Kotlin 2.2.21 or later to compile its ML Kit dependency. Install expo-build-properties before creating an Android development build:

Terminal
npx expo install expo-build-properties

If you are installing this in an existing React Native app, make sure to install expo in your project.

Then add the config plugin to your app configuration. Keep higher compatible versions if your app already configures them:

Example app.json with config plugin

app.json
{ "expo": { "plugins": [ [ "expo-build-properties", { "android": { "minSdkVersion": 26, "kotlinVersion": "2.2.21" } } ] ] } }

The plugin writes these Android settings during Prebuild. Installing or importing expo-ai does not change native build settings by itself. These build requirements do not establish device support for Gemini Nano; check model availability at runtime.

Are you using this library in an existing React Native app?

If you're not using Continuous Native Generation (CNG) or you're using a native android project manually, then set the app's minimum SDK to 26 or later and use Kotlin 2.2.21 or later in the root project's Kotlin Gradle plugin dependency.

If the app's minimum SDK is lower than 26, the Android build fails while merging manifests:

uses-sdk:minSdkVersion 24 cannot be smaller than version 26 declared in library [:expo-ai] Suggestion: use a compatible library with a minSdk of at most 24, or increase this project's minSdk version to at least 26, or use tools:overrideLibrary="expo.modules.ai" to force usage (may lead to runtime failures)

Raise the app's minimum SDK to 26, as shown above. Do not apply the tools:overrideLibrary suggestion. It silences the check without changing the requirement, and the ML Kit dependency still needs Android 8.0 or later at runtime.

Generate text

Call generateAsync() with a prompt. Read the completed text from result.value:

import { generateAsync } from 'expo-ai'; export async function suggestTitle(notes: string) { const result = await generateAsync(notes, { instructions: 'Suggest a short title for these notes.', }); return result.value; }

Every successful one-shot helper returns the same result envelope. In addition to value, it identifies the selected provider and model, reports the output format, and includes token usage when the provider makes it available:

const result = await generateAsync('Suggest a short title for my notes.'); console.log({ title: result.value, provider: result.provider, model: result.model, format: result.format, // 'text', 'constrained', or 'validated' usage: result.usage, });

Calling getAvailabilityAsync() first is optional. Generation proceeds when the model is ready and rejects with the actual readiness or availability error otherwise. Generation does not request model preparation. On web, browser-managed assets have an additional preparation caveat. Separate calls have separate histories, and the package releases their temporary sessions on success, failure, or cancellation.

On Android, the package passes instructions as a native system instruction when the device's model supports it. Otherwise, it includes them in the prompt, where they have weaker priority than a native system instruction.

Summarize and categorize

summarizeAsync() and categorizeAsync() add task instructions to the same generation pipeline. They return the same result envelope and accept the applicable generation options, including cancellation, updates, and deadlines:

import { categorizeAsync, summarizeAsync } from 'expo-ai'; export async function organizeNote(text: string) { const summary = await summarizeAsync(text, { length: 'short' }); const category = await categorizeAsync(text, { categories: ['work', 'personal', 'other'], }); return { summary: summary.value, category: category.value, // 'work' | 'personal' | 'other' }; }

Summary length can be short, medium, or long and defaults to medium. It is a generation preference, not an exact word count. Categorization requires a nonempty list of categories and validates the result against that list. The package uses native constrained output when supported and library validation with bounded repairs otherwise. Both helpers use the same provider and execution controls as generateAsync().

Generate structured values

Pass schema to request a structured result. The package automatically uses native schema constraints when the provider supports them and validates the completed value in JavaScript. Otherwise, it requests JSON and validates the response, with bounded repair attempts. A successful request returns a validated value; failure rejects with an error. The optional schema helpers infer the result's TypeScript type without requiring as const:

import { generateAsync, schema } from 'expo-ai'; const noteSchema = schema.object({ category: schema.enum(['work', 'personal', 'other']), summary: schema.string({ description: 'A one-sentence summary.' }), confidence: schema.number({ minimum: 0, maximum: 1 }), suggestedTitle: schema.optional(schema.string()), }); export async function describeNote(text: string) { const result = await generateAsync(text, { schema: noteSchema }); return result.value; // { category: 'work' | 'personal' | 'other'; summary: string; suggestedTitle?: string } }

Objects created with schema.object() are closed, with every field required unless wrapped in schema.optional(). The helpers produce the same supported JSON Schema dialect as plain schema objects. You can still pass a plain schema; use as const satisfies ModelSchema to preserve literal types when declaring one separately.

Schema helpers

HelperResult
schema.string(options?)A string schema.
schema.enum(values, options?)A string schema restricted to a nonempty list of values.
schema.number(options?)A finite number with optional inclusive bounds.
schema.integer(options?)A safe integer with optional inclusive bounds.
schema.boolean(options?)A boolean schema.
schema.array(items, options?)An array with the supplied item schema.
schema.object(properties, options?)A closed object with the supplied property schemas.
schema.optional(propertySchema)An optional property marker for schema.object().

All options accept description. Number and integer options also accept inclusive minimum and maximum bounds. Integer bounds must be safe JavaScript integers. Array options accept minItems and maxItems. An optional property marker can only be used inside schema.object(); it is not a standalone schema.

The exported InferSchema<S> type maps a schema to its result type, including string enums and optional object fields. S represents the schema's TypeScript type. For example, InferSchema<typeof noteSchema> gives the same type as result.value above.

Supported schema subset

The package accepts a limited JSON Schema dialect:

  • Strings, including nonempty string enums.
  • Finite numbers and safe integers, with optional inclusive minimum and maximum bounds, and booleans.
  • Arrays with an item schema and optional minItems and maxItems bounds.
  • Objects with declared properties, an optional required list, and additionalProperties: false.
  • Optional descriptions on each schema.

Fields omitted from a plain object's required list are optional. Unsupported keywords and invalid schemas reject before generation. String patterns, null, unions, references, and recursive schemas are not supported. Validation checks the shape of a value; it does not establish factual accuracy or whether the content meets your app's needs.

Validation and repair limits

Every structured result is validated in JavaScript. The generation path depends on the platform and whether its model can apply the requested schema constraints:

PlatformGeneration and validation path
AppleUses native constrained output when the selected model supports the schema, then validates the completed value in JavaScript. Otherwise, requests JSON and uses library validation with bounded repairs.
AndroidRequests JSON and uses library validation with bounded repairs.
WebUses browser-native constrained output for schemas it supports, then validates in JavaScript. Schemas with numeric bounds use library validation and bounded repairs instead.

Apple Foundation Models expresses numeric bounds as native generation guides. Android and web use the JSON-and-repair path for schemas containing numeric bounds. This path gives the model weaker guidance than native constrained output and can fail after exhausting the repair budget. The final value must still pass the same inclusive bounds during JavaScript validation on every platform.

Use maximumRetries to bound additional library repair attempts per response. This option does not change which provider capabilities are used. Provider failures and refusals end the task; they do not trigger a retry through a weaker generation path:

import { generateAsync, schema } from 'expo-ai'; export async function detectQuestion(text: string) { const result = await generateAsync(`Is this text a question? ${text}`, { schema: schema.boolean(), maximumRetries: 1, maximumSteps: 4, }); return result.value; }

maximumRetries defaults to 1 and accepts integers from 0 to 3. Set it to 0 to reject the first invalid response without a repair attempt. Native constrained output does not use these library repairs.

maximumSteps limits model completions initiated by the library, including repairs. The default budget is 8, and explicit values accept integers from 1 to 32. An explicit limit rejects when native tools are used because the provider does not expose its internal model calls. Use maximumToolCalls to limit handler starts across providers.

Show generation updates

Pass an optional onUpdate callback to display a response as it is generated. Each update contains the complete current text snapshot. Replace the displayed text with the snapshot instead of appending it. The returned promise supplies the final result:

import { generateAsync } from 'expo-ai'; export async function previewSummary(text: string, setPreview: (text: string) => void) { const result = await generateAsync(`Summarize: ${text}`, { onUpdate: ({ text }) => setPreview(text), }); setPreview(result.value); return result.value; }

Updates are provisional. For structured generation, wait for the final validated result.value before using the value. Tasks using library validation or tool orchestration may produce no intermediate text updates. If onUpdate throws or returns a promise that rejects, the task rejects with ERR_UPDATE_FAILED.

Supply application tools

Pass ordinary tool definition objects through tools. Each definition has a name, description, object input schema, and handler. Use ToolDefinition<S> for a reusable typed definition, where S is the input schema's type:

import { generateAsync, schema, type ToolDefinition } from 'expo-ai'; const categoryInput = schema.object({ category: schema.enum(['work', 'personal']), }); const lookupCategory: ToolDefinition<typeof categoryInput> = { name: 'lookupCategory', description: 'Read the description of a category.', inputSchema: categoryInput, execute: ({ category }, { signal }) => { if (signal.aborted) { throw new Error('Tool canceled.'); } return { category, description: category === 'work' ? 'Projects and professional tasks.' : 'Home and leisure.', }; }, }; export async function explainCategory() { const result = await generateAsync('Look up and explain the work category.', { tools: [lookupCategory], maximumToolCalls: 2, }); return result.value; }

The package uses native tool orchestration when supported. Otherwise, it asks the model to propose calls and manages the loop in JavaScript. Arguments are validated before approval or execution on every provider. In compatible browsers, the library retains native schema constraints for generated tool decisions and final values while managing tool execution itself.

The exported ToolDefinition<S> type describes these fields:

FieldTypeDescription
namestringA unique name of 1–64 letters, digits, or underscores, starting with a letter or underscore.
descriptionstringA nonempty explanation of when the model should use the tool.
inputSchemaSA supported object schema for the tool's arguments.
execute(input: InferSchema<S>, context: ToolContext) => unknownA synchronous or asynchronous handler returning JSON-compatible data.

Tool definitions are validated and copied when a task starts. Handlers can return data directly or through a promise. Strings are passed through as text. Plain objects, arrays, finite numbers, booleans, and null are serialized for the provider. Unsupported values, such as undefined, functions, big integers, class instances, or circular structures, reject instead of silently losing data.

Approve a tool call

Supplied tools run automatically unless you provide beforeTool. This optional callback receives the tool name, validated arguments, call ID, and cancellation signal. Return true to allow the call or false to reject the task with ERR_TOOL_DENIED without running the handler. The callback can return a promise while your app displays an approval interface:

import { generateAsync, type ToolCall, type ToolDefinition } from 'expo-ai'; export async function runWithApproval( prompt: string, tools: ToolDefinition[], requestApproval: (call: ToolCall) => Promise<boolean> ) { return await generateAsync(prompt, { tools, beforeTool: requestApproval, }); }

Observe the callback's signal to close pending approval UI when the task is canceled. When a deadline is supplied, waiting for approval counts toward it.

Tool execution limits

maximumToolCalls applies to native and library tool orchestration, defaults to 4, and accepts integers from 0 to 16. It limits how many handlers can start during one request. A value of 0 prevents handler execution.

A handler failure ends the task. The package does not retry the handler or execute the same call ID twice. Completed tool observations remain available during library repair attempts. A model can still request a similar action with a new call ID, so use application-level idempotency when repeating an action would be harmful.

Use images and Apple tools

Pass local image files in images. Import predefined tool objects from expo-ai/apple to read text, barcodes, or QR codes:

import { generateAsync, schema } from 'expo-ai'; import { Tools } from 'expo-ai/apple'; export async function readReceipt(uri: string) { return await generateAsync('Read the receipt image and return its total.', { images: [{ uri, label: 'receipt' }], tools: [Tools.ocr], schema: schema.object({ total: schema.string({ description: 'The total, including the currency shown.', }), }), }); }

Tools.ocr and Tools.barcode are ordinary tool definitions. Pass either or both alongside your own tools. They use Apple Vision on iOS 26 and later and macOS 26 and later. Their tool arguments contain an image label identifying a file attached to the current request. OCR returns { text: string } to the model. Barcode recognition returns text payloads as { barcodes: [{ payload: string, symbology: string }] }. An image with no recognized content produces empty text or an empty array.

The existing beforeTool, maximumToolCalls, cancellation, and deadline options apply. Approval completes before the image tool starts recognition. Tools cannot read an image by inventing a file path or by referring to a previous request's label. Android and web reject these Apple tools with ERR_UNSUPPORTED_FEATURE.

On iOS 26, the language model receives the labels and the image tool's recognized data; it cannot see the image pixels. On iOS 27, the package uses native image attachments when the system model reports vision support. For general visual descriptions, require images support:

import { generateAsync } from 'expo-ai'; export async function describeImage(uri: string) { return await generateAsync('Describe the image in one sentence.', { images: [{ uri }], requires: ['images'], }); }

requires: ['images'] checks model vision. requires: ['imageTools'] checks Apple image tool support. A text-only Apple model can use attached images when an image tool is supplied. Without model vision or an image tool, an image request rejects with ERR_UNSUPPORTED_FEATURE.

Supply up to eight file:// URLs. Network URLs, data URLs, and file URLs pointing to another host are rejected. Each label must be unique within the request and contain 1–128 characters without control characters. Omitted labels become image-1, image-2, and so on. Keep the files available until the request finishes. Native image attachments retain decoded images in successful session history; changing the original file does not change a retained attachment. The same public label can be reused in another request.

Read token usage

Successful results include usage. Counts remain null when the provider cannot report them. When built with Xcode 27 or later, the package returns measured input and output counts on iOS 27 and macOS 27, plus cached input and reasoning counts when reported by Foundation Models. Cached and reasoning counts are included in their corresponding totals.

On iOS 26.4 and macOS 26.4 or later, usage.contextTokens reports the token count of the completed context when counting succeeds. It describes context occupancy, not the number of input tokens consumed by that request. Earlier Apple versions, Android, and web leave unavailable measurements unknown.

import { generateAsync } from 'expo-ai'; export async function suggestTitleWithUsage(notes: string) { const result = await generateAsync(notes, { instructions: 'Suggest a short title.', }); return { title: result.value, inputTokens: result.usage.inputTokens, outputTokens: result.usage.outputTokens, contextTokens: result.usage.contextTokens ?? null, }; }

For library repair loops, input and output counts are added across model calls only when every call reports them. Context tokens describe the final call's context and are not added across independent calls. Token counting does not trigger another generation, and an unavailable counter does not fail an otherwise successful result.

Cancel a task or set a deadline

Both cancellation and deadlines are optional. Pass an AbortSignal to cancel a task. Set timeoutMs to impose an overall deadline, including session creation, generation, tool handlers, waiting for approval, and library repairs. When omitted, the library imposes no deadline. Provider limits and tool or repair budgets still apply.

import { generateAsync } from 'expo-ai'; export async function summarizeWithCancellation(text: string, signal: AbortSignal) { const result = await generateAsync(`Summarize: ${text}`, { signal, timeoutMs: 60_000, }); return result.value; }

Pass a controller's signal to this function and call AbortController.abort() from your app's cancel action. Handle the returned promise even when canceling. Cancellation rejects with ERR_ABORTED; an expired deadline rejects with ERR_TIMEOUT. Explicit deadlines accept integer values from 1 to 2147483647 milliseconds.

Cancellation stops updates, reaches pending approval and tool handlers, and releases the temporary session. Handlers should pass the signal to cancellable work and check it before starting an action. Cancellation cannot forcibly stop arbitrary application code or undo an action that a tool has already performed.

Check availability and handle errors

Use getAvailabilityAsync() when readiness or capability information helps your interface:

import { getAvailabilityAsync } from 'expo-ai'; export async function canCategorize() { const availability = await getAvailabilityAsync(); return availability.status === 'available'; }

When the result is unavailable, inspect its reason. A downloadable result means the model can be downloaded, downloading means preparation is in progress, and not-ready means it is not ready to use. An available result includes the provider's capabilities. A capability can be supported, unsupported, or unknown. Model identifiers, context limits, and token usage remain null when the provider does not report them.

Capability requirements describe native provider support. For example, requires: ['constrainedOutput'] requires native schema constraints. On Android, requiring constrainedOutput or runtimeToolDeclarations returns unavailable even though the library can perform structured tasks and orchestrate tools. On web, requiring runtimeToolDeclarations also returns unavailable, while library tool orchestration is supported. Include these requirements only when your app needs that native guarantee.

The Android SDK does not expose a supported-language query. Explicit inputLanguages or outputLanguage requirements return unavailable with reason language-support-unknown; this does not mean the language itself is unsupported. Requests without language requirements can proceed.

Public operations reject with LanguageModelError, which has a stable code, a readable message, and an underlying cause when available. Handle codes instead of parsing error messages. A readiness failure can show a preparation button:

import { generateAsync, LanguageModelError } from 'expo-ai'; export async function generateWhenReady(prompt: string, offerPreparation: () => void) { try { return await generateAsync(prompt); } catch (error) { if (error instanceof LanguageModelError && error.code === 'ERR_MODEL_NOT_READY') { offerPreparation(); } throw error; } }

Preparation is explicit and provider-dependent. Android can request the system model download when allowDownload: true. On web, call preparation from a user action such as a button press so the browser has the required user activation. A catch handler that runs after generation fails may no longer have that activation. After preparation reports available, your app can retry the task.

Apple's adapter checks readiness again, but Apple manages model preparation through system settings. It cannot start a download or report download progress, even with allowDownload: true. Do not treat an unsupported device or cancellation as a preparation request.

Omitting allowDownload or setting it to false only checks current availability. It does not start a download or wait for one to finish.

Show preparation progress

Call prepareAsync() from an explicit preparation action in your app. Pass onProgress to update the interface and signal to cancel the app's preparation request:

import { prepareAsync } from 'expo-ai'; export async function prepareModel( signal: AbortSignal, setProgress: (progress: number | null) => void ) { const availability = await prepareAsync({ allowDownload: true, signal, onProgress: setProgress, }); return availability.status === 'available'; }

The progress callback is optional on Android and web. Progress is a number from 0 to 1, or null when a fraction is unavailable. Display an indeterminate indicator for null. If the progress callback throws or returns a promise that rejects, preparation rejects with ERR_PREPARATION_FAILED.

Cancellation stops the app's preparation request and progress updates. The system may continue a download it already owns. Check availability again before deciding whether another preparation action is needed.

On web, generation checks that the browser reports available before creating a session. However, the browser's LanguageModel.create() API has no option that forbids asset downloads. If readiness changes between those calls, the adapter can only abort when it observes unexpected download progress. It cannot guarantee that the browser transfers no assets or stops a download it already manages.

Error codeMeaning
ERR_MODEL_NOT_READYThe model is supported but not ready to generate.
ERR_MODEL_UNAVAILABLEThe requested model or capabilities are unavailable.
ERR_PREPARATION_FAILEDModel preparation or its progress callback failed.
ERR_ABORTEDThe caller canceled the task.
ERR_APP_BACKGROUNDAndroid work stopped because the app entered the background.
ERR_TIMEOUTThe task deadline or provider time limit expired.
ERR_TOOL_DENIEDThe approval callback rejected a tool call.
ERR_RESPONSE_INVALIDThe completed response failed validation.
ERR_VALIDATION_RETRIES_EXHAUSTEDLibrary validation repairs exhausted their budget.
ERR_CONTEXT_WINDOW_EXCEEDEDThe request exceeds the model's available context.
ERR_UPDATE_FAILEDThe update callback threw an error.

Other typed codes cover invalid options and schemas, tool failures, and execution limits. Inspect cause when diagnosing a provider failure.

Use an explicit session

Use createSessionAsync() when a task needs conversation history across requests. Top-level functions remain independent; an explicit session retains its own successful history. Call dispose() when you finish:

import { createSessionAsync } from 'expo-ai'; export async function refineTitle(notes: string) { const session = await createSessionAsync({ instructions: 'Suggest concise note titles.', }); try { await session.generateAsync(notes); const result = await session.generateAsync('Make that title shorter.'); return result.value; } finally { session.dispose(); } }

Apple uses Foundation Models sessions. On Android, the package serializes successful turns as JSON and includes them in each Prompt API request. Library orchestration keeps its own successful history in JavaScript. Longer histories consume more of the model's input budget.

On web, the adapter generates on a clone of the committed browser session and commits successful model completions. If the browser reports context overflow, the task rejects with ERR_CONTEXT_WINDOW_EXCEEDED rather than silently discarding history.

Only one generation can run in a session at a time. The package does not silently truncate conversation history. A provider can commit a model response before a later JavaScript validation or update callback fails. Completed tool effects outside the session cannot be undone.

dispose() is synchronous and safe to call more than once. It releases the session and rejects active work with ERR_SESSION_DISPOSED.

Iterate over a stream

An explicit session also provides generateStream(), which returns an async iterable. Text events contain cumulative snapshots, and a successful stream ends with one validated result event:

import { createSessionAsync } from 'expo-ai'; export async function streamSummary(text: string, onText: (text: string) => void) { const session = await createSessionAsync(); try { for await (const event of session.generateStream(`Summarize: ${text}`)) { if (event.type === 'text') { onText(event.text); } else if (event.type === 'result') { onText(event.result.value); } } } finally { session.dispose(); } }

Breaking out of the loop cancels that generation. Stream failures throw from the iterator. A slow consumer can skip intermediate text snapshots while still receiving tool events and the final result.

API

import * as ExpoAI from 'expo-ai';

Constants

ExpoAI.schema

Experimental
 • 
Android
iOS
macOS
Web

Optional helpers for the supported JSON Schema dialect. Objects are closed and properties are required unless wrapped in schema.optional(...). Helpers preserve literal types without as const. Supported plain JSON schemas can also be used directly or combined with helpers. See the schema helper reference for supported methods and options.

Classes

LanguageModelError

Experimental
 • 
Android
iOS
macOS
Web

Type: Class extends Error

A language model failure with a stable machine-readable code.

LanguageModelError Properties

code

Experimental
 • 
Android
iOS
macOS
Web
Read only • Type: LanguageModelErrorCode

Identifies the failure independently of its human-readable message.

LanguageModelSession

Experimental
 • 
Android
iOS 26.0+
macOS 26.0+
Web

A local language model conversation. One generation may run at a time. Always dispose a session when its owning screen or task ends.

LanguageModelSession Properties

capabilities

Experimental
 • 
Android
iOS 26.0+
macOS 26.0+
Web
Read only • Type: ModelCapabilities

Capabilities of the selected provider. Compatibility never changes native support flags.

LanguageModelSession Methods

dispose()

Experimental
 • 
Android
iOS
macOS
Web

Aborts pending generation and tool callbacks and releases the native session. Repeated calls are harmless. Already-started tool effects cannot be undone.

Returns:
void

generateAsync(prompt, options)

Experimental
 • 
Android
iOS
macOS
Web
Overload #1
ParameterType
promptstring
optionsStructuredRequest<S>

Generates and validates a complete structured result.

generateAsync(prompt, options)

Experimental
 • 
Android
iOS
macOS
Web
Overload #2
ParameterType
promptstring
options(optional)TextRequestOptions

Generates a complete text response. Failures reject rather than returning partial success.

Returns:
Promise<GenerationResult<string>>

generateStream(prompt, options)

Experimental
 • 
Android
iOS
macOS
Web
Overload #1
ParameterType
promptstring
optionsStructuredRequest<S>

Streams full text snapshots followed by exactly one validated result. Breaking iteration aborts generation.

generateStream(prompt, options)

Experimental
 • 
Android
iOS
macOS
Web
Overload #2
ParameterType
promptstring
options(optional)TextRequestOptions

Streams text snapshots. Tool and generation failures throw from the iterator.

Methods

ExpoAI.categorizeAsync(input, options)

Experimental
 • 
Android
iOS 26.0+
macOS 26.0+
Web
ParameterType
inputstring
optionsCategorizeOptions<C, T>

Assigns text to one supplied category and validates the selected label. Uses native constrained output when available, otherwise validates generated output with bounded repair attempts. Invalid categories never return as success.

Returns:
Promise<GenerationResult<C[number]>>

ExpoAI.createSessionAsync(options)

Experimental
 • 
Android
iOS 26.0+
macOS 26.0+
Web
ParameterType
options(optional)SessionOptions<T>

Creates a session using the on-device system model. Rejects when the requirements are unmet. Does not request model preparation or select a cloud provider. On Web, browser-managed assets can change after the readiness check. Tools use native orchestration when supported, otherwise a bounded library loop.

ExpoAI.generateAsync(input, options)

Experimental
 • 
Android
iOS 26.0+
macOS 26.0+
Web
Overload #1
ParameterType
inputstring
optionsStructuredGenerateOptions<S, T>

Performs one independent task and validates the result against a schema. Uses native constrained output when available, otherwise validates generated output with bounded repair attempts. Provider failures do not trigger fallback.

ExpoAI.generateAsync(input, options)

Experimental
 • 
Android
iOS 26.0+
macOS 26.0+
Web
Overload #2
ParameterType
inputstring
options(optional)GenerateOptions<T>

Performs one independent local model task. Availability checks are optional; an unready model rejects without requesting preparation. The temporary session is disposed on success, failure, or cancellation.

Returns:
Promise<GenerationResult<string>>

ExpoAI.getAvailabilityAsync(requirements)

Experimental
 • 
Android
iOS
macOS
Web
ParameterType
requirements(optional)ModelRequirements

Checks whether the requested system model is ready, without starting a download. Apple Intelligence must be enabled and its system model ready on supported hardware. Availability can change; callers must also handle generation failures. Android requires supported Gemini Nano hardware and ML Kit model readiness. Web support depends on the browser's local Prompt API and model readiness.

ExpoAI.prepareAsync(options)

Experimental
 • 
Android
iOS
macOS
Web
ParameterType
options(optional)ModelRequirements & { allowDownload: boolean, onProgress: (progress: number | null) => void, signal: AbortSignal }

Explicitly prepares a system model. Android downloads require allowDownload: true and a foreground app. Progress is a fraction from 0 to 1, or null when unknown. Cancellation stops this request's work; it does not remove shared model assets. Apple manages model preparation through system settings; this adapter cannot trigger its download or report progress, even when allowDownload is true. Browser model assets are managed by the browser; Web preparation requires a user gesture when it needs to create the browser model.

ExpoAI.summarizeAsync(input, options)

Experimental
 • 
Android
iOS 26.0+
macOS 26.0+
Web
ParameterType
inputstring
options(optional)SummarizeOptions<T>

Summarizes text using the same generation, tool, update, and cancellation behavior as generateAsync. Each call owns an independent session. Summary length is a generation preference.

Returns:
Promise<GenerationResult<string>>

Types

ArraySchemaOptions

Android
iOS
macOS
Web

Bounds the number of items in an array.

Type: SchemaOptions extended by:

PropertyTypeDescription
maxItems(optional)number
-
minItems(optional)number
-

CapabilitySupport

Android
iOS
macOS
Web

Literal type: string

Support for a feature on the currently selected provider and model.

Acceptable values are: 'supported' | 'unsupported' | 'unknown'

CategorizeOptions

Android
iOS
macOS
Web

Options for selecting exactly one of the supplied categories.

Type: Omit<StructuredGenerateOptions<ModelSchema, T>, 'schema'> extended by:

PropertyTypeDescription
categoriesC

Distinct, nonempty category labels. The result is one of these exact strings.

GenerateOptions

Android
iOS
macOS
Web

Literal type: union

Options for one independent plain text task.

Acceptable values are: SessionOptions<Schemas> | TextRequestOptions | GenerationUpdateOptions

GenerationEvent

Android
iOS
macOS
Web

Text events are complete snapshots. Structured snapshots are raw text until the final result validates; partially parsed objects are not exposed yet.

Type: object shaped as below:

PropertyTypeDescription
textstring
-
type'text'
-

Or object shaped as below:

PropertyTypeDescription
callIdstring
-
toolNamestring
-
type'tool-start'
-

Or object shaped as below:

PropertyTypeDescription
callIdstring
-
toolNamestring
-
type'tool-end'
-

Or object shaped as below:

PropertyTypeDescription
resultGenerationResult<T>
-
type'result'
-

GenerationResult

Android
iOS
macOS
Web

A complete successful result. Validation checks structure, not factual accuracy.

PropertyTypeDescription
format'text' | 'constrained' | 'validated'
-
modelstring | null
-
providerstring
-
usageGenerationUsage

Unknown measurements remain null rather than being estimated.

valueT
-

GenerationUpdate

Android
iOS
macOS
Web

A provisional, cumulative text snapshot. Replace the previous preview.

PropertyTypeDescription
textstring
-

GenerationUpdateOptions

Android
iOS
macOS
Web

Options shared by one-shot requests.

PropertyTypeDescription
onUpdate(optional)(update: GenerationUpdate) => void

Receives provisional full text snapshots; the promise provides the validated final result.

GenerationUsage

Android
iOS
macOS
Web

Provider-reported token counts. Unknown measurements are null; optional fields are omitted when unavailable.

PropertyTypeDescription
cachedInputTokens(optional)number | null

Cached tokens included in inputTokens, when reported by the provider.

contextTokens(optional)number | null

Tokens in the final model call's completed context. This is not billed or per-request input usage.

inputTokensnumber | null
-
outputTokensnumber | null
-
reasoningTokens(optional)number | null

Reasoning tokens included in outputTokens, when reported by the provider.

ImageInput

Android
iOS
macOS
Web

An app-provided image file. Network and data URLs are not accepted.

PropertyTypeDescription
label(optional)string

Unique within the request, 1–128 characters without control characters. Tools refer to this label.

uristring
-

InferSchema<S>

Android
iOS
macOS
Web

Infers the validated result of a literal schema, including optional fields.

Generic: S

Type: S ? V : undefined

LanguageModelErrorCode

Android
iOS
macOS
Web

Literal type: string

Stable failure codes shared by preparation, sessions, and one-shot tasks.

Acceptable values are: 'ERR_ABORTED' | 'ERR_APP_BACKGROUND' | 'ERR_AVAILABILITY_FAILED' | 'ERR_COMPLETION_FAILED' | 'ERR_COMPLETION_INVALID' | 'ERR_CONTEXT_WINDOW_EXCEEDED' | 'ERR_GENERATION_FAILED' | 'ERR_MODEL_NOT_READY' | 'ERR_MODEL_REFUSAL' | 'ERR_MODEL_UNAVAILABLE' | 'ERR_OPTIONS_INVALID' | 'ERR_PREPARATION_FAILED' | 'ERR_PROVIDER_RESPONSE_INVALID' | 'ERR_RATE_LIMITED' | 'ERR_RESPONSE_INVALID' | 'ERR_SCHEMA_UNSUPPORTED' | 'ERR_SESSION_BUSY' | 'ERR_SESSION_DISPOSED' | 'ERR_STEP_LIMIT' | 'ERR_TIMEOUT' | 'ERR_TOOL_CALL_LIMIT' | 'ERR_TOOL_CALL_REPLAY' | 'ERR_TOOL_DECISION_FAILED' | 'ERR_TOOL_DECISION_INVALID' | 'ERR_TOOL_DENIED' | 'ERR_TOOL_EVENT_FAILED' | 'ERR_TOOL_FAILED' | 'ERR_TOOL_UNKNOWN' | 'ERR_UNSUPPORTED_FEATURE' | 'ERR_UNSUPPORTED_LANGUAGE' | 'ERR_UPDATE_FAILED' | 'ERR_VALIDATION_RETRIES_EXHAUSTED'

ModelAvailability

Android
iOS
macOS
Web

Readiness of the requested local model. Checking readiness never starts a download.

Type: object shaped as below:

PropertyTypeDescription
capabilitiesModelCapabilities
-
status'available'
-

Or object shaped as below:

PropertyTypeDescription
progressnumber | null
-
status'downloadable' | 'downloading' | 'not-ready'
-

Or object shaped as below:

PropertyTypeDescription
reasonstring
-
status'unavailable'
-

ModelCapabilities

Android
iOS
macOS
Web

Availability is provider- and model-specific, not an OS-version guarantee.

PropertyTypeDescription
constrainedOutputCapabilitySupport
-
contextTokensnumber | null
-
execution'on-device'
-
imagesCapabilitySupport
-
imageTools(optional)CapabilitySupport

Native OCR and barcode tools for labeled local images. Omitted by older providers.

modelstring | null
-
providerstring
-
runtimeToolDeclarationsCapabilitySupport
-

ModelRequirements

Android
iOS
macOS
Web

Requirements are checked during availability, preparation, and session creation.

PropertyTypeDescription
inputLanguages(optional)readonly string[]

Explicit language support requirements. Android currently reports language-support-unknown when supplied.

outputLanguage(optional)string

Explicit output language requirement. Omit on Android while support cannot be verified.

provider(optional)'system'

Selects the system provider. Downloadable third-party backends are not included.

requires(optional)readonly ('constrainedOutput' | 'runtimeToolDeclarations' | 'images' | 'imageTools')[]
-

ModelSchema

Experimental
 • 
Android
iOS
macOS
Web

The supported JSON Schema subset. Unknown keywords reject before generation. Objects are closed. Required fields must name declared properties. Numeric bounds are inclusive. Null, unions, references, and string patterns are not supported yet.

A schema may nest up to 32 levels deep and hold up to 1024 nodes in total, counting the root, every property and every array item schema. Both limits reject on every platform rather than only on the provider that enforces them.

Type: { enum: readonly [string, ...string[]], type: 'string' } | { maximum: number, minimum: number, type: 'number' } | { maximum: number, minimum: number, type: 'integer' } | { type: 'boolean' } | { items: ModelSchema, maxItems: number, minItems: number, type: 'array' } | { additionalProperties: false, properties: Readonly<Record<string, ModelSchema>>, required: readonly string[], type: 'object' } extended by:

PropertyTypeDescription
description(optional)string
-

NumericSchemaOptions

Android
iOS
macOS
Web

Inclusively bounds a numeric value.

Type: SchemaOptions extended by:

PropertyTypeDescription
maximum(optional)number
-
minimum(optional)number
-

ObjectSchema

Android
iOS
macOS
Web

Type: Extract<ModelSchema, { type: 'object' }>

A closed object schema used for tool arguments.

RequestOptions

Android
iOS
macOS
Web

Controls one generation and all model/tool work it initiates.

PropertyTypeDescription
beforeTool(optional)(call: ToolCall) => boolean | Promise<boolean>

Return true to allow a validated tool call, or false to end the generation without executing it.

images(optional)readonly ImageInput[]

Local image files. Up to 8; labels default to image-1, image-2, and so on. Apple image tools support iOS 26; model vision requires iOS 27 and model support.

maximumOutputTokens(optional)number

Maximum output tokens per model call. Unsupported explicit options reject.

maximumRetries(optional)number

Maximum additional library validation repair attempts per response; integers 0–3, default 1. Does not select a weaker implementation. Provider failures and tool handlers are never retried.

maximumSteps(optional)number

Maximum library model calls, including repairs; integers 1–32. Compatibility loops default to 8. Rejects with native tools because their internal model calls cannot be counted.

maximumToolCalls(optional)number

Maximum handler starts. Defaults to 4; integers 0–16. Applies to native and compatibility tools.

signal(optional)AbortSignal
-
timeoutMs(optional)number

Optional overall deadline in milliseconds, including tool handlers and approval. No library deadline applies when omitted.

SchemaOptions

Android
iOS
macOS
Web

Describes a schema value to the model.

PropertyTypeDescription
description(optional)string
-

SessionOptions

Android
iOS
macOS
Web

Options used to create a session.

Type: ModelRequirements extended by:

PropertyTypeDescription
instructions(optional)string
-
tools(optional)ToolDefinitions<Schemas>
-

StructuredGenerateOptions

Android
iOS
macOS
Web

Literal type: union

Options for one independent task with a final schema-validated value.

Acceptable values are: SessionOptions<Schemas> | StructuredRequest<S> | GenerationUpdateOptions

StructuredRequest

Android
iOS
macOS
Web

A request for a final schema-validated value.

Type: RequestOptions extended by:

PropertyTypeDescription
schemaS
-

SummarizeOptions

Android
iOS
macOS
Web

Options for summarizing text in an independent task.

Type: GenerateOptions<T> extended by:

PropertyTypeDescription
length(optional)'short' | 'medium' | 'long'

Relative summary length. Defaults to medium; this is a model preference, not a size guarantee.

TextRequestOptions

Android
iOS
macOS
Web

Plain text requests cannot contain structured generation options.

Type: RequestOptions extended by:

PropertyTypeDescription
schema(optional)never
-

ToolCall

Android
iOS
macOS
Web

Complete, validated arguments supplied to the application's action interceptor.

Type: ToolContext extended by:

PropertyTypeDescription
argumentsunknown
-
namestring
-

ToolContext

Android
iOS
macOS
Web

A tool handler's request identity and cooperative cancellation signal.

PropertyTypeDescription
callIdstring
-
signalAbortSignal
-

ToolDefinition<S>

Experimental
 • 
Android
iOS
macOS
Web

An application tool. Arguments validate before execution. Return ordinary JSON-compatible data; strings remain text and other values are serialized. Unsupported values reject the generation. Both synchronous and asynchronous handlers are supported. Honor the signal when possible; cancellation cannot undo completed effects.

Generic: S

Type: S ? { description: string, inputSchema: S, name: string, } : never