expo-ai-kit 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,10 @@ Runs **Apple Foundation Models** (iOS 26+), **ML Kit** (Android), and downloadab
10
10
  — with streaming, structured output, tool calling, **embeddings & on-device RAG**,
11
11
  cancellation, and runtime model switching, all on-device.
12
12
 
13
+ **New in 0.11:** a [**Vercel AI SDK provider**](#vercel-ai-sdk) — use `generateText`,
14
+ `streamText`, and `embed` from the [AI SDK](https://ai-sdk.dev) with on-device models
15
+ via `expo-ai-kit/ai`. Same `useChat`-style code you'd write for OpenAI, no API key.
16
+
13
17
  ## Install
14
18
 
15
19
  ```bash
@@ -129,6 +133,63 @@ throws `DEVICE_NOT_SUPPORTED` (MediaPipe support is planned). The toolkit —
129
133
  `chunkText`, `cosineSimilarity`, and the `createVectorStore` (add / search top-k / `toJSON`
130
134
  for persistence) — is pure JS and works on **both platforms with any vector source**.
131
135
 
136
+ ## Vercel AI SDK
137
+
138
+ expo-ai-kit ships a first-class [AI SDK](https://ai-sdk.dev) provider: point `model:` at
139
+ `expoAiKit()` and the AI SDK's `generateText` / `streamText` / `generateObject` / `embed`
140
+ run **on-device** — reuse code, examples, and patterns written for cloud providers, with
141
+ no API key and no data leaving the phone.
142
+
143
+ ```bash
144
+ npm i ai # AI SDK 6+ (the provider implements LanguageModelV3; AI SDK 7 accepts it too)
145
+ ```
146
+
147
+ ```tsx
148
+ import { generateText, streamText, embed } from 'ai';
149
+ import { expoAiKit } from 'expo-ai-kit/ai';
150
+
151
+ // Text — the active on-device model (OS built-in by default)
152
+ const { text } = await generateText({
153
+ model: expoAiKit(),
154
+ prompt: 'Capital of France?',
155
+ });
156
+
157
+ // Streaming — pass a specific model id to activate it first
158
+ const result = streamText({
159
+ model: expoAiKit('gemma-e2b'), // must be downloaded; settings ride along:
160
+ // model: expoAiKit('gemma-e2b', { generation: { temperature: 0.7 } }),
161
+ prompt: 'Write a short story',
162
+ });
163
+ for await (const chunk of result.textStream) setText((t) => t + chunk);
164
+
165
+ // Embeddings (iOS) — the same NLContextualEmbedding behind embed()
166
+ const { embedding } = await embed({
167
+ model: expoAiKit.embeddingModel(),
168
+ value: 'sunny day at the beach',
169
+ });
170
+ ```
171
+
172
+ Tool calling and `generateObject` work too — they ride the exact same prompt protocol as
173
+ the core `generateText`/`generateObject`, so behavior is identical either way.
174
+
175
+ **On-device caveats** (the honest fine print):
176
+
177
+ - **One generation at a time.** Concurrent calls reject with `INFERENCE_BUSY` — on-device
178
+ models share a single KV-cache. Await one call before starting the next.
179
+ - **Sampling is fixed at model activation**, not per call. A per-call `temperature`/`topK`
180
+ is reported as an AI SDK warning and ignored; set it via `expoAiKit(id, { generation })`
181
+ or `setModel()`.
182
+ - **Streaming buffers when tools or JSON output are requested** — the tool-call envelope
183
+ has to be parsed whole, not leaked as text deltas. Plain-text streaming is token-by-token.
184
+ - **No token usage numbers** (on-device runtimes don't report them) and no image/file inputs
185
+ (vision is on the roadmap). `embeddingModel()` is iOS-only, like `embed()`.
186
+ - React Native needs the AI SDK's usual polyfills (e.g. `web-streams-polyfill`,
187
+ `structuredClone`, `TextEncoder`) — see the docs for a copy-paste setup.
188
+
189
+ The provider is a thin wrapper over the same core functions below — mix and match freely.
190
+ The core stays **zero-dependency**; `@ai-sdk/provider` is an optional peer used only for
191
+ types when you import `expo-ai-kit/ai`.
192
+
132
193
  ## Downloadable models
133
194
 
134
195
  Beyond the OS built-ins, you can download open models (via LiteRT-LM) and switch to them at
@@ -198,6 +259,7 @@ file persists on disk, so its `'downloaded'` status survives restarts once re-re
198
259
 
199
260
  Inference: `isAvailable`, `sendMessage`, `streamMessage`, `generateObject`, `generateText`.
200
261
  Embeddings & RAG: `embed`, `chunkText`, `cosineSimilarity`, `createVectorStore`.
262
+ AI SDK provider (`expo-ai-kit/ai`): `expoAiKit`, `createExpoAiKit`.
201
263
  Models: `getBuiltInModels`, `getDownloadableModels`, `getRecommendedModel`,
202
264
  `downloadModel`, `cancelDownload`, `deleteModel`, `setModel`, `unloadModel`, `getActiveModel`.
203
265
  Custom models: `registerModel`, `unregisterModel`, `getRegisteredModels`, `fetchModelMetadata`.
package/ai.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './build/ai';
package/ai.js ADDED
@@ -0,0 +1,5 @@
1
+ // Subpath entry: `import { expoAiKit } from 'expo-ai-kit/ai'`.
2
+ // A root shim (instead of a package.json "exports" map) so resolution works
3
+ // across Metro, Node, and TypeScript without changing how the existing
4
+ // entry points resolve.
5
+ export * from './build/ai';
@@ -0,0 +1,55 @@
1
+ import type { LanguageModelV3CallOptions, LanguageModelV3ToolResultOutput, LanguageModelV3Usage, SharedV3Warning } from '@ai-sdk/provider';
2
+ import { type LLMMessage } from '../types';
3
+ /**
4
+ * On-device runtimes report no token counts, so every usage field is
5
+ * `undefined` (the shape the spec prescribes for "unknown").
6
+ */
7
+ export declare const EMPTY_USAGE: LanguageModelV3Usage;
8
+ /** Unique-enough id for stream parts / tool calls within a session. */
9
+ export declare function newPartId(prefix: string): string;
10
+ /** Everything doGenerate/doStream needs, derived from LanguageModelV3CallOptions. */
11
+ export type ConvertedCall = {
12
+ /** The conversation in our core protocol, instructions merged into the system message. */
13
+ messages: LLMMessage[];
14
+ /** Names of function tools offered to the model ([] when none / toolChoice 'none'). */
15
+ toolNames: string[];
16
+ /** True when responseFormat is JSON — output should be extracted as JSON. */
17
+ jsonMode: boolean;
18
+ /** Spec warnings for settings we cannot honor on-device. */
19
+ warnings: SharedV3Warning[];
20
+ };
21
+ /**
22
+ * Map the AI SDK's call options onto our core `LLMMessage[]` protocol.
23
+ *
24
+ * - System messages are merged into one leading system message, with the tool
25
+ * instruction (tools.ts) and/or JSON-Schema instruction (structured.ts)
26
+ * appended — the same prompt protocol as generateText / generateObject.
27
+ * - Assistant tool-call parts are re-rendered as the `{"tool", "arguments"}`
28
+ * envelope and tool results as formatToolResult() text, so multi-step AI SDK
29
+ * conversations round-trip through the exact format the model was taught.
30
+ * - Per-call sampling settings can't be honored (fixed at setModel) and are
31
+ * reported as spec warnings instead of being silently dropped.
32
+ *
33
+ * @throws {ModelError} DEVICE_NOT_SUPPORTED on file/image prompt parts —
34
+ * on-device text models have no media input (vision is on the roadmap).
35
+ */
36
+ export declare function convertCallOptions(options: LanguageModelV3CallOptions): ConvertedCall;
37
+ /** What the model's raw text turned out to be, per the offered capabilities. */
38
+ export type ExtractedOutput = {
39
+ kind: 'text';
40
+ text: string;
41
+ } | {
42
+ kind: 'tool-call';
43
+ toolName: string;
44
+ input: string;
45
+ };
46
+ /**
47
+ * Interpret raw model output for the AI SDK: detect a tool-call envelope when
48
+ * tools were offered (reusing parseToolCall), or extract/clean the JSON value
49
+ * in json mode (reusing extractJson). Single-shot — the repair loops live in
50
+ * the core generateText/generateObject, not in the provider.
51
+ */
52
+ export declare function extractOutput(text: string, toolNames: string[], jsonMode: boolean): ExtractedOutput;
53
+ /** Unwrap the spec's tool-result output union into a plain value for formatToolResult. */
54
+ export declare function unwrapToolOutput(output: LanguageModelV3ToolResultOutput): unknown;
55
+ //# sourceMappingURL=convert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/ai/convert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAE1B,+BAA+B,EAC/B,oBAAoB,EACpB,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAA+B,KAAK,UAAU,EAAgB,MAAM,UAAU,CAAC;AAkBtF;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,oBAYzB,CAAC;AAGF,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,qFAAqF;AACrF,MAAM,MAAM,aAAa,GAAG;IAC1B,0FAA0F;IAC1F,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,uFAAuF;IACvF,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,6EAA6E;IAC7E,QAAQ,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAeF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,0BAA0B,GAAG,aAAa,CAqIrF;AAED,gFAAgF;AAChF,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EAAE,EACnB,QAAQ,EAAE,OAAO,GAChB,eAAe,CAsBjB;AAED,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,+BAA+B,GAAG,OAAO,CAiBjF"}
@@ -0,0 +1,270 @@
1
+ import { ModelError } from '../types';
2
+ import { buildSchemaInstruction, extractJson } from '../structured';
3
+ import { buildToolInstruction, formatToolResult, parseToolCall } from '../tools';
4
+ // ---------------------------------------------------------------------------
5
+ // Pure helpers for the Vercel AI SDK provider (src/ai/index.ts).
6
+ //
7
+ // Like structured.ts and tools.ts, this module imports no native module so its
8
+ // logic can be unit-tested in plain Node. It maps the AI SDK's LanguageModelV3
9
+ // call options onto our LLMMessage[] protocol — reusing the exact same tool
10
+ // instruction / tool-call envelope as generateText() and the same JSON-Schema
11
+ // instruction / extraction as generateObject(), so a model behaves identically
12
+ // whether it's driven through the AI SDK or through the core API.
13
+ //
14
+ // Only *type* imports from '@ai-sdk/provider' are allowed here (they compile
15
+ // away), keeping the published JS free of runtime dependencies.
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * On-device runtimes report no token counts, so every usage field is
19
+ * `undefined` (the shape the spec prescribes for "unknown").
20
+ */
21
+ export const EMPTY_USAGE = {
22
+ inputTokens: {
23
+ total: undefined,
24
+ noCache: undefined,
25
+ cacheRead: undefined,
26
+ cacheWrite: undefined,
27
+ },
28
+ outputTokens: {
29
+ total: undefined,
30
+ text: undefined,
31
+ reasoning: undefined,
32
+ },
33
+ };
34
+ let idCounter = 0;
35
+ /** Unique-enough id for stream parts / tool calls within a session. */
36
+ export function newPartId(prefix) {
37
+ return `${prefix}_${Date.now()}_${++idCounter}`;
38
+ }
39
+ // Per-call sampling knobs the AI SDK passes that on-device runtimes fix at
40
+ // setModel() time (LiteRT-LM builds its sampler at conversation creation).
41
+ const FIXED_AT_SETMODEL = [
42
+ 'temperature',
43
+ 'topP',
44
+ 'topK',
45
+ 'seed',
46
+ 'maxOutputTokens',
47
+ 'stopSequences',
48
+ 'presencePenalty',
49
+ 'frequencyPenalty',
50
+ ];
51
+ /**
52
+ * Map the AI SDK's call options onto our core `LLMMessage[]` protocol.
53
+ *
54
+ * - System messages are merged into one leading system message, with the tool
55
+ * instruction (tools.ts) and/or JSON-Schema instruction (structured.ts)
56
+ * appended — the same prompt protocol as generateText / generateObject.
57
+ * - Assistant tool-call parts are re-rendered as the `{"tool", "arguments"}`
58
+ * envelope and tool results as formatToolResult() text, so multi-step AI SDK
59
+ * conversations round-trip through the exact format the model was taught.
60
+ * - Per-call sampling settings can't be honored (fixed at setModel) and are
61
+ * reported as spec warnings instead of being silently dropped.
62
+ *
63
+ * @throws {ModelError} DEVICE_NOT_SUPPORTED on file/image prompt parts —
64
+ * on-device text models have no media input (vision is on the roadmap).
65
+ */
66
+ export function convertCallOptions(options) {
67
+ const warnings = [];
68
+ for (const setting of FIXED_AT_SETMODEL) {
69
+ if (options[setting] != null) {
70
+ warnings.push({
71
+ type: 'unsupported',
72
+ feature: setting,
73
+ details: 'On-device sampling is fixed when the model is activated (setModel / provider settings), not per call.',
74
+ });
75
+ }
76
+ }
77
+ // --- Tools → the generateText() text protocol -----------------------------
78
+ const toolSet = {};
79
+ const toolChoice = options.toolChoice ?? { type: 'auto' };
80
+ if (options.tools && toolChoice.type !== 'none') {
81
+ for (const tool of options.tools) {
82
+ if (tool.type === 'function') {
83
+ toolSet[tool.name] = {
84
+ description: tool.description ?? '',
85
+ parameters: toLocalSchema(tool.inputSchema),
86
+ };
87
+ }
88
+ else {
89
+ warnings.push({
90
+ type: 'unsupported',
91
+ feature: `provider tool "${tool.id}"`,
92
+ details: 'expo-ai-kit runs on-device models; provider-executed tools are not available.',
93
+ });
94
+ }
95
+ }
96
+ }
97
+ const toolNames = Object.keys(toolSet);
98
+ let toolInstruction = toolNames.length > 0 ? buildToolInstruction(toolSet) : '';
99
+ if (toolNames.length > 0 && toolChoice.type === 'required') {
100
+ toolInstruction += '\n- For this request you MUST call one of the tools.';
101
+ warnings.push({
102
+ type: 'compatibility',
103
+ feature: "toolChoice: 'required'",
104
+ details: 'Enforced via a prompt nudge — on-device models have no constrained decoding yet.',
105
+ });
106
+ }
107
+ else if (toolNames.length > 0 && toolChoice.type === 'tool') {
108
+ toolInstruction += `\n- For this request you MUST call the tool "${toolChoice.toolName}".`;
109
+ warnings.push({
110
+ type: 'compatibility',
111
+ feature: `toolChoice: { type: 'tool' }`,
112
+ details: 'Enforced via a prompt nudge — on-device models have no constrained decoding yet.',
113
+ });
114
+ }
115
+ // --- responseFormat json → the generateObject() instruction ---------------
116
+ let jsonMode = false;
117
+ let schemaInstruction = '';
118
+ if (options.responseFormat?.type === 'json') {
119
+ if (toolNames.length > 0) {
120
+ // The SDK never combines them; if a caller does, tools win.
121
+ warnings.push({
122
+ type: 'other',
123
+ message: 'responseFormat: json is ignored when tools are provided.',
124
+ });
125
+ }
126
+ else {
127
+ jsonMode = true;
128
+ schemaInstruction = options.responseFormat.schema
129
+ ? buildSchemaInstruction(toLocalSchema(options.responseFormat.schema))
130
+ : 'Respond with ONLY a valid JSON value — no prose, no markdown code fences.';
131
+ }
132
+ }
133
+ // --- Prompt → LLMMessage[] -------------------------------------------------
134
+ const systemParts = [];
135
+ const messages = [];
136
+ for (const message of options.prompt) {
137
+ switch (message.role) {
138
+ case 'system': {
139
+ systemParts.push(message.content);
140
+ break;
141
+ }
142
+ case 'user': {
143
+ const text = message.content
144
+ .map((part) => {
145
+ if (part.type === 'text')
146
+ return part.text;
147
+ throw unsupportedPart(part.type);
148
+ })
149
+ .join('');
150
+ messages.push({ role: 'user', content: text });
151
+ break;
152
+ }
153
+ case 'assistant': {
154
+ const chunks = [];
155
+ for (const part of message.content) {
156
+ if (part.type === 'text') {
157
+ chunks.push(part.text);
158
+ }
159
+ else if (part.type === 'tool-call') {
160
+ // Re-render past calls in the envelope the model was taught, so
161
+ // multi-step conversations stay in-protocol.
162
+ chunks.push(JSON.stringify({ tool: part.toolName, arguments: part.input ?? {} }));
163
+ }
164
+ else if (part.type === 'reasoning') {
165
+ // Model-internal; not replayed.
166
+ }
167
+ else if (part.type === 'tool-result') {
168
+ // Provider-executed results never occur here (we execute nothing).
169
+ }
170
+ else {
171
+ throw unsupportedPart(part.type);
172
+ }
173
+ }
174
+ const text = chunks.join('\n');
175
+ if (text !== '')
176
+ messages.push({ role: 'assistant', content: text });
177
+ break;
178
+ }
179
+ case 'tool': {
180
+ const chunks = [];
181
+ for (const part of message.content) {
182
+ if (part.type === 'tool-result') {
183
+ chunks.push(formatToolResult(part.toolName, unwrapToolOutput(part.output)));
184
+ }
185
+ // tool-approval-response: we never emit approval requests, so none
186
+ // can legitimately appear; ignore defensively.
187
+ }
188
+ if (chunks.length > 0)
189
+ messages.push({ role: 'user', content: chunks.join('\n\n') });
190
+ break;
191
+ }
192
+ }
193
+ }
194
+ const instructions = [toolInstruction, schemaInstruction].filter((s) => s !== '');
195
+ const system = [...systemParts, ...instructions].join('\n\n');
196
+ if (system !== '') {
197
+ messages.unshift({ role: 'system', content: system });
198
+ }
199
+ return { messages, toolNames, jsonMode, warnings };
200
+ }
201
+ /**
202
+ * Interpret raw model output for the AI SDK: detect a tool-call envelope when
203
+ * tools were offered (reusing parseToolCall), or extract/clean the JSON value
204
+ * in json mode (reusing extractJson). Single-shot — the repair loops live in
205
+ * the core generateText/generateObject, not in the provider.
206
+ */
207
+ export function extractOutput(text, toolNames, jsonMode) {
208
+ if (toolNames.length > 0) {
209
+ const parsed = parseToolCall(text, toolNames);
210
+ if (parsed.kind === 'tool') {
211
+ return {
212
+ kind: 'tool-call',
213
+ toolName: parsed.toolName,
214
+ input: safeStringify(parsed.args ?? {}),
215
+ };
216
+ }
217
+ // 'unknown-tool' deliberately falls through to text: the provider is
218
+ // single-shot, so surfacing the raw output beats silently retrying.
219
+ return { kind: 'text', text };
220
+ }
221
+ if (jsonMode) {
222
+ const parsed = extractJson(text);
223
+ if (parsed.ok)
224
+ return { kind: 'text', text: safeStringify(parsed.value) };
225
+ return { kind: 'text', text }; // let the SDK report the parse failure honestly
226
+ }
227
+ return { kind: 'text', text };
228
+ }
229
+ /** Unwrap the spec's tool-result output union into a plain value for formatToolResult. */
230
+ export function unwrapToolOutput(output) {
231
+ switch (output.type) {
232
+ case 'text':
233
+ return output.value;
234
+ case 'json':
235
+ return output.value;
236
+ case 'error-text':
237
+ return { error: output.value };
238
+ case 'error-json':
239
+ return { error: output.value };
240
+ case 'execution-denied':
241
+ return { error: `Tool execution denied${output.reason ? `: ${output.reason}` : ''}` };
242
+ case 'content':
243
+ return output.value
244
+ .map((part) => (part.type === 'text' ? part.text : `[${part.type} omitted]`))
245
+ .join('\n');
246
+ }
247
+ }
248
+ /**
249
+ * The spec's JSONSchema7 and our pragmatic JSONSchema subset are structurally
250
+ * compatible for prompting purposes (both serialize to the same JSON); this
251
+ * cast localizes the type-system disagreement to one place.
252
+ */
253
+ function toLocalSchema(schema) {
254
+ if (typeof schema === 'object' && schema !== null)
255
+ return schema;
256
+ return {};
257
+ }
258
+ function unsupportedPart(type) {
259
+ return new ModelError('DEVICE_NOT_SUPPORTED', '', `expo-ai-kit AI SDK provider: "${type}" prompt parts are not supported — ` +
260
+ 'on-device models take text only (vision input is on the roadmap).');
261
+ }
262
+ function safeStringify(value) {
263
+ try {
264
+ return JSON.stringify(value) ?? String(value);
265
+ }
266
+ catch {
267
+ return String(value);
268
+ }
269
+ }
270
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/ai/convert.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n LanguageModelV3CallOptions,\n LanguageModelV3FunctionTool,\n LanguageModelV3ToolResultOutput,\n LanguageModelV3Usage,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport { ModelError, type JSONSchema, type LLMMessage, type ToolSet } from '../types';\nimport { buildSchemaInstruction, extractJson } from '../structured';\nimport { buildToolInstruction, formatToolResult, parseToolCall } from '../tools';\n\n// ---------------------------------------------------------------------------\n// Pure helpers for the Vercel AI SDK provider (src/ai/index.ts).\n//\n// Like structured.ts and tools.ts, this module imports no native module so its\n// logic can be unit-tested in plain Node. It maps the AI SDK's LanguageModelV3\n// call options onto our LLMMessage[] protocol — reusing the exact same tool\n// instruction / tool-call envelope as generateText() and the same JSON-Schema\n// instruction / extraction as generateObject(), so a model behaves identically\n// whether it's driven through the AI SDK or through the core API.\n//\n// Only *type* imports from '@ai-sdk/provider' are allowed here (they compile\n// away), keeping the published JS free of runtime dependencies.\n// ---------------------------------------------------------------------------\n\n/**\n * On-device runtimes report no token counts, so every usage field is\n * `undefined` (the shape the spec prescribes for \"unknown\").\n */\nexport const EMPTY_USAGE: LanguageModelV3Usage = {\n inputTokens: {\n total: undefined,\n noCache: undefined,\n cacheRead: undefined,\n cacheWrite: undefined,\n },\n outputTokens: {\n total: undefined,\n text: undefined,\n reasoning: undefined,\n },\n};\n\nlet idCounter = 0;\n/** Unique-enough id for stream parts / tool calls within a session. */\nexport function newPartId(prefix: string): string {\n return `${prefix}_${Date.now()}_${++idCounter}`;\n}\n\n/** Everything doGenerate/doStream needs, derived from LanguageModelV3CallOptions. */\nexport type ConvertedCall = {\n /** The conversation in our core protocol, instructions merged into the system message. */\n messages: LLMMessage[];\n /** Names of function tools offered to the model ([] when none / toolChoice 'none'). */\n toolNames: string[];\n /** True when responseFormat is JSON — output should be extracted as JSON. */\n jsonMode: boolean;\n /** Spec warnings for settings we cannot honor on-device. */\n warnings: SharedV3Warning[];\n};\n\n// Per-call sampling knobs the AI SDK passes that on-device runtimes fix at\n// setModel() time (LiteRT-LM builds its sampler at conversation creation).\nconst FIXED_AT_SETMODEL = [\n 'temperature',\n 'topP',\n 'topK',\n 'seed',\n 'maxOutputTokens',\n 'stopSequences',\n 'presencePenalty',\n 'frequencyPenalty',\n] as const;\n\n/**\n * Map the AI SDK's call options onto our core `LLMMessage[]` protocol.\n *\n * - System messages are merged into one leading system message, with the tool\n * instruction (tools.ts) and/or JSON-Schema instruction (structured.ts)\n * appended — the same prompt protocol as generateText / generateObject.\n * - Assistant tool-call parts are re-rendered as the `{\"tool\", \"arguments\"}`\n * envelope and tool results as formatToolResult() text, so multi-step AI SDK\n * conversations round-trip through the exact format the model was taught.\n * - Per-call sampling settings can't be honored (fixed at setModel) and are\n * reported as spec warnings instead of being silently dropped.\n *\n * @throws {ModelError} DEVICE_NOT_SUPPORTED on file/image prompt parts —\n * on-device text models have no media input (vision is on the roadmap).\n */\nexport function convertCallOptions(options: LanguageModelV3CallOptions): ConvertedCall {\n const warnings: SharedV3Warning[] = [];\n\n for (const setting of FIXED_AT_SETMODEL) {\n if (options[setting] != null) {\n warnings.push({\n type: 'unsupported',\n feature: setting,\n details:\n 'On-device sampling is fixed when the model is activated (setModel / provider settings), not per call.',\n });\n }\n }\n\n // --- Tools → the generateText() text protocol -----------------------------\n const toolSet: ToolSet = {};\n const toolChoice = options.toolChoice ?? { type: 'auto' };\n if (options.tools && toolChoice.type !== 'none') {\n for (const tool of options.tools) {\n if (tool.type === 'function') {\n toolSet[tool.name] = {\n description: tool.description ?? '',\n parameters: toLocalSchema((tool as LanguageModelV3FunctionTool).inputSchema),\n };\n } else {\n warnings.push({\n type: 'unsupported',\n feature: `provider tool \"${tool.id}\"`,\n details: 'expo-ai-kit runs on-device models; provider-executed tools are not available.',\n });\n }\n }\n }\n const toolNames = Object.keys(toolSet);\n\n let toolInstruction = toolNames.length > 0 ? buildToolInstruction(toolSet) : '';\n if (toolNames.length > 0 && toolChoice.type === 'required') {\n toolInstruction += '\\n- For this request you MUST call one of the tools.';\n warnings.push({\n type: 'compatibility',\n feature: \"toolChoice: 'required'\",\n details: 'Enforced via a prompt nudge — on-device models have no constrained decoding yet.',\n });\n } else if (toolNames.length > 0 && toolChoice.type === 'tool') {\n toolInstruction += `\\n- For this request you MUST call the tool \"${toolChoice.toolName}\".`;\n warnings.push({\n type: 'compatibility',\n feature: `toolChoice: { type: 'tool' }`,\n details: 'Enforced via a prompt nudge — on-device models have no constrained decoding yet.',\n });\n }\n\n // --- responseFormat json → the generateObject() instruction ---------------\n let jsonMode = false;\n let schemaInstruction = '';\n if (options.responseFormat?.type === 'json') {\n if (toolNames.length > 0) {\n // The SDK never combines them; if a caller does, tools win.\n warnings.push({\n type: 'other',\n message: 'responseFormat: json is ignored when tools are provided.',\n });\n } else {\n jsonMode = true;\n schemaInstruction = options.responseFormat.schema\n ? buildSchemaInstruction(toLocalSchema(options.responseFormat.schema))\n : 'Respond with ONLY a valid JSON value — no prose, no markdown code fences.';\n }\n }\n\n // --- Prompt → LLMMessage[] -------------------------------------------------\n const systemParts: string[] = [];\n const messages: LLMMessage[] = [];\n\n for (const message of options.prompt) {\n switch (message.role) {\n case 'system': {\n systemParts.push(message.content);\n break;\n }\n case 'user': {\n const text = message.content\n .map((part) => {\n if (part.type === 'text') return part.text;\n throw unsupportedPart(part.type);\n })\n .join('');\n messages.push({ role: 'user', content: text });\n break;\n }\n case 'assistant': {\n const chunks: string[] = [];\n for (const part of message.content) {\n if (part.type === 'text') {\n chunks.push(part.text);\n } else if (part.type === 'tool-call') {\n // Re-render past calls in the envelope the model was taught, so\n // multi-step conversations stay in-protocol.\n chunks.push(JSON.stringify({ tool: part.toolName, arguments: part.input ?? {} }));\n } else if (part.type === 'reasoning') {\n // Model-internal; not replayed.\n } else if (part.type === 'tool-result') {\n // Provider-executed results never occur here (we execute nothing).\n } else {\n throw unsupportedPart(part.type);\n }\n }\n const text = chunks.join('\\n');\n if (text !== '') messages.push({ role: 'assistant', content: text });\n break;\n }\n case 'tool': {\n const chunks: string[] = [];\n for (const part of message.content) {\n if (part.type === 'tool-result') {\n chunks.push(formatToolResult(part.toolName, unwrapToolOutput(part.output)));\n }\n // tool-approval-response: we never emit approval requests, so none\n // can legitimately appear; ignore defensively.\n }\n if (chunks.length > 0) messages.push({ role: 'user', content: chunks.join('\\n\\n') });\n break;\n }\n }\n }\n\n const instructions = [toolInstruction, schemaInstruction].filter((s) => s !== '');\n const system = [...systemParts, ...instructions].join('\\n\\n');\n if (system !== '') {\n messages.unshift({ role: 'system', content: system });\n }\n\n return { messages, toolNames, jsonMode, warnings };\n}\n\n/** What the model's raw text turned out to be, per the offered capabilities. */\nexport type ExtractedOutput =\n | { kind: 'text'; text: string }\n | { kind: 'tool-call'; toolName: string; input: string };\n\n/**\n * Interpret raw model output for the AI SDK: detect a tool-call envelope when\n * tools were offered (reusing parseToolCall), or extract/clean the JSON value\n * in json mode (reusing extractJson). Single-shot — the repair loops live in\n * the core generateText/generateObject, not in the provider.\n */\nexport function extractOutput(\n text: string,\n toolNames: string[],\n jsonMode: boolean\n): ExtractedOutput {\n if (toolNames.length > 0) {\n const parsed = parseToolCall(text, toolNames);\n if (parsed.kind === 'tool') {\n return {\n kind: 'tool-call',\n toolName: parsed.toolName,\n input: safeStringify(parsed.args ?? {}),\n };\n }\n // 'unknown-tool' deliberately falls through to text: the provider is\n // single-shot, so surfacing the raw output beats silently retrying.\n return { kind: 'text', text };\n }\n\n if (jsonMode) {\n const parsed = extractJson(text);\n if (parsed.ok) return { kind: 'text', text: safeStringify(parsed.value) };\n return { kind: 'text', text }; // let the SDK report the parse failure honestly\n }\n\n return { kind: 'text', text };\n}\n\n/** Unwrap the spec's tool-result output union into a plain value for formatToolResult. */\nexport function unwrapToolOutput(output: LanguageModelV3ToolResultOutput): unknown {\n switch (output.type) {\n case 'text':\n return output.value;\n case 'json':\n return output.value;\n case 'error-text':\n return { error: output.value };\n case 'error-json':\n return { error: output.value };\n case 'execution-denied':\n return { error: `Tool execution denied${output.reason ? `: ${output.reason}` : ''}` };\n case 'content':\n return output.value\n .map((part) => (part.type === 'text' ? part.text : `[${part.type} omitted]`))\n .join('\\n');\n }\n}\n\n/**\n * The spec's JSONSchema7 and our pragmatic JSONSchema subset are structurally\n * compatible for prompting purposes (both serialize to the same JSON); this\n * cast localizes the type-system disagreement to one place.\n */\nfunction toLocalSchema(schema: unknown): JSONSchema {\n if (typeof schema === 'object' && schema !== null) return schema as JSONSchema;\n return {};\n}\n\nfunction unsupportedPart(type: string): ModelError {\n return new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n '',\n `expo-ai-kit AI SDK provider: \"${type}\" prompt parts are not supported — ` +\n 'on-device models take text only (vision input is on the roadmap).'\n );\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n"]}
@@ -0,0 +1,66 @@
1
+ import type { EmbeddingModelV3, LanguageModelV3 } from '@ai-sdk/provider';
2
+ import { type SetModelOptions } from '../types';
3
+ /**
4
+ * Sentinel model id meaning "whatever model is currently active" — the OS
5
+ * built-in by default, or whatever the app last activated via setModel().
6
+ * The provider never switches models for this id.
7
+ */
8
+ export declare const AUTO_MODEL_ID = "auto";
9
+ /** Model id for the built-in embedding model (Apple NLContextualEmbedding, iOS 17+). */
10
+ export declare const EMBEDDING_MODEL_ID = "apple-nl-contextual";
11
+ /**
12
+ * Per-instance model settings, applied when this instance activates its model
13
+ * (same shape as {@link setModel}'s options). If the model is already active,
14
+ * it is NOT reloaded — on-device model loads are expensive, so sampling config
15
+ * effectively belongs to whoever activated the model first.
16
+ */
17
+ export type ExpoAiKitModelSettings = SetModelOptions;
18
+ export interface ExpoAiKitProvider {
19
+ /** Shorthand for {@link ExpoAiKitProvider.languageModel}. */
20
+ (modelId?: string, settings?: ExpoAiKitModelSettings): LanguageModelV3;
21
+ /**
22
+ * A LanguageModelV3 over the on-device model. Pass any model id accepted by
23
+ * setModel() ('apple-fm', 'mlkit', 'gemma-e2b', a registerModel() id, …) to
24
+ * have the provider activate it before generating, or omit / pass 'auto' to
25
+ * use whatever model is already active.
26
+ */
27
+ languageModel(modelId?: string, settings?: ExpoAiKitModelSettings): LanguageModelV3;
28
+ /** An EmbeddingModelV3 over embed() — iOS-only (Apple NLContextualEmbedding). */
29
+ embeddingModel(modelId?: string): EmbeddingModelV3;
30
+ /** @deprecated Spec alias for {@link ExpoAiKitProvider.embeddingModel}. */
31
+ textEmbeddingModel(modelId?: string): EmbeddingModelV3;
32
+ /** expo-ai-kit has no image models; always throws MODEL_NOT_FOUND. */
33
+ imageModel(modelId: string): never;
34
+ }
35
+ /**
36
+ * Create an expo-ai-kit provider for the Vercel AI SDK.
37
+ *
38
+ * ```ts
39
+ * import { generateText, streamText } from 'ai';
40
+ * import { expoAiKit } from 'expo-ai-kit/ai';
41
+ *
42
+ * const { text } = await generateText({
43
+ * model: expoAiKit(), // the active on-device model
44
+ * prompt: 'Capital of France?',
45
+ * });
46
+ *
47
+ * const result = streamText({
48
+ * model: expoAiKit('gemma-e2b'), // activates gemma-e2b first (must be downloaded)
49
+ * prompt: 'Write a short story',
50
+ * });
51
+ * ```
52
+ *
53
+ * On-device caveats (all documented in the guide):
54
+ * - One generation at a time — concurrent calls reject with INFERENCE_BUSY.
55
+ * - Per-call sampling (temperature, topK, …) is reported as an unsupported-
56
+ * setting warning; sampling is fixed when the model is activated.
57
+ * - Tool calling and JSON output ride the same prompt protocol as
58
+ * generateText()/generateObject() — single-shot here, since the AI SDK owns
59
+ * the loop. Streaming buffers when tools/JSON are requested (the envelope
60
+ * must be parsed whole, not surfaced as text deltas).
61
+ * - embeddingModel() is iOS-only for now; Android throws DEVICE_NOT_SUPPORTED.
62
+ */
63
+ export declare function createExpoAiKit(): ExpoAiKitProvider;
64
+ /** The default provider instance. */
65
+ export declare const expoAiKit: ExpoAiKitProvider;
66
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAMhB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC;AAkB5D;;;;GAIG;AACH,eAAO,MAAM,aAAa,SAAS,CAAC;AAEpC,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG,eAAe,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,sBAAsB,GAAG,eAAe,CAAC;IACvE;;;;;OAKG;IACH,aAAa,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,sBAAsB,GAAG,eAAe,CAAC;IACpF,iFAAiF;IACjF,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACnD,2EAA2E;IAC3E,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACvD,sEAAsE;IACtE,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,eAAe,IAAI,iBAAiB,CAyBnD;AAED,qCAAqC;AACrC,eAAO,MAAM,SAAS,EAAE,iBAAqC,CAAC"}