expo-ai-kit 0.9.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
@@ -7,8 +7,12 @@ On-device AI for Expo & React Native — run LLMs locally. No API keys, no cloud
7
7
 
8
8
  Runs **Apple Foundation Models** (iOS 26+), **ML Kit** (Android), and downloadable
9
9
  **Gemma 4** (E2B / E4B, iOS + Android via [LiteRT-LM](https://ai.google.dev/edge/litert-lm))
10
- — with streaming, structured output, tool calling, cancellation, and runtime model switching,
11
- all on-device.
10
+ — with streaming, structured output, tool calling, **embeddings & on-device RAG**,
11
+ cancellation, and runtime model switching, all on-device.
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.
12
16
 
13
17
  ## Install
14
18
 
@@ -98,6 +102,94 @@ Omit a tool's `execute` to gate it yourself: the loop stops with `finishReason:
98
102
  and hands you the proposed call to confirm before running. Keep tool sets small and parameter
99
103
  schemas flat — on-device models pick tools more reliably that way.
100
104
 
105
+ ## Embeddings & RAG
106
+
107
+ Turn text into vectors for semantic search and retrieval-augmented generation — find the
108
+ chunks of your own documents most relevant to a question, then feed them to the model. All
109
+ on-device: your data never leaves the phone.
110
+
111
+ ```tsx
112
+ import { embed, chunkText, createVectorStore, sendMessage } from 'expo-ai-kit';
113
+
114
+ // 1. Index your document once: split → embed → store
115
+ const chunks = chunkText(document); // overlapping, sentence-aware chunks
116
+ const { embeddings } = await embed(chunks); // one vector per chunk
117
+ const store = createVectorStore<{ text: string }>();
118
+ store.addMany(chunks.map((text, i) => ({ id: `c${i}`, vector: embeddings[i], metadata: { text } })));
119
+
120
+ // 2. At query time: embed the question, retrieve the top matches, answer from them
121
+ const { embeddings: [q] } = await embed([question]);
122
+ const context = store.search(q, { topK: 4 }).map((h) => h.metadata!.text).join('\n\n');
123
+
124
+ const { text } = await sendMessage([
125
+ { role: 'system', content: `Answer using only this context:\n${context}` },
126
+ { role: 'user', content: question },
127
+ ]);
128
+ ```
129
+
130
+ `embed()` is backed by Apple's `NLContextualEmbedding` — a **zero-download, OS-maintained**
131
+ model (iOS 17+, works even without Apple Intelligence). **iOS-only for now**; on Android it
132
+ throws `DEVICE_NOT_SUPPORTED` (MediaPipe support is planned). The toolkit —
133
+ `chunkText`, `cosineSimilarity`, and the `createVectorStore` (add / search top-k / `toJSON`
134
+ for persistence) — is pure JS and works on **both platforms with any vector source**.
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
+
101
193
  ## Downloadable models
102
194
 
103
195
  Beyond the OS built-ins, you can download open models (via LiteRT-LM) and switch to them at
@@ -166,6 +258,8 @@ file persists on disk, so its `'downloaded'` status survives restarts once re-re
166
258
  ## API
167
259
 
168
260
  Inference: `isAvailable`, `sendMessage`, `streamMessage`, `generateObject`, `generateText`.
261
+ Embeddings & RAG: `embed`, `chunkText`, `cosineSimilarity`, `createVectorStore`.
262
+ AI SDK provider (`expo-ai-kit/ai`): `expoAiKit`, `createExpoAiKit`.
169
263
  Models: `getBuiltInModels`, `getDownloadableModels`, `getRecommendedModel`,
170
264
  `downloadModel`, `cancelDownload`, `deleteModel`, `setModel`, `unloadModel`, `getActiveModel`.
171
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';
@@ -128,6 +128,20 @@ class ExpoAiKitModule : Module() {
128
128
  activeStreamJobs.remove(sessionId)
129
129
  }
130
130
 
131
+ // ==================================================================
132
+ // Embeddings
133
+ // ==================================================================
134
+ // iOS-only for now (Apple NLContextualEmbedding). The JS layer guards the
135
+ // platform and throws before reaching native, so this is a defensive stub
136
+ // that honors the same "CODE::reason" error contract. Android support via
137
+ // MediaPipe Text Embedder is planned.
138
+ AsyncFunction("embed") { _: List<String> ->
139
+ throw RuntimeException(
140
+ "DEVICE_NOT_SUPPORTED::On-device embeddings are iOS-only for now " +
141
+ "(Apple NLContextualEmbedding); Android support via MediaPipe is planned"
142
+ )
143
+ }
144
+
131
145
  // ==================================================================
132
146
  // Model discovery
133
147
  // ==================================================================
@@ -18,6 +18,10 @@ export interface ExpoAiKitNativeModule {
18
18
  sendMessage(messages: LLMMessage[], systemPrompt: string, sessionId: string): Promise<LLMResponse>;
19
19
  startStreaming(messages: LLMMessage[], systemPrompt: string, sessionId: string): Promise<void>;
20
20
  stopStreaming(sessionId: string): Promise<void>;
21
+ embed(texts: string[]): Promise<{
22
+ embeddings: number[][];
23
+ dimensions: number;
24
+ }>;
21
25
  getBuiltInModels(): BuiltInModel[];
22
26
  getDownloadableModelStatus(modelId: string): Promise<DownloadableModelStatus>;
23
27
  getDeviceRamBytes(): number;
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAiKitModule.d.ts","sourceRoot":"","sources":["../src/ExpoAiKitModule.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EACL,YAAY,EACZ,uBAAuB,EACvB,UAAU,EACV,WAAW,EACX,cAAc,EACd,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,kBAAkB,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAChE,kBAAkB,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC5D,CAAC;AAEF,8FAA8F;AAC9F,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,qBAAqB;IAEpC,WAAW,IAAI,OAAO,CAAC;IAEvB,WAAW,CACT,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAAC;IACxB,cAAc,CACZ,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGhD,gBAAgB,IAAI,YAAY,EAAE,CAAC;IAGnC,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC9E,iBAAiB,IAAI,MAAM,CAAC;IAM5B,QAAQ,CACN,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,sBAAsB,GACjC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,cAAc,IAAI,MAAM,CAAC;IAGzB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7B,aAAa,CACX,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAG5C,WAAW,CAAC,CAAC,SAAS,MAAM,qBAAqB,EAC/C,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GACjC,iBAAiB,CAAC;CACtB;AAED,QAAA,MAAM,eAAe,uBACoC,CAAC;AAE1D,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"ExpoAiKitModule.d.ts","sourceRoot":"","sources":["../src/ExpoAiKitModule.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EACL,YAAY,EACZ,uBAAuB,EACvB,UAAU,EACV,WAAW,EACX,cAAc,EACd,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,kBAAkB,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAChE,kBAAkB,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC5D,CAAC;AAEF,8FAA8F;AAC9F,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,qBAAqB;IAEpC,WAAW,IAAI,OAAO,CAAC;IAEvB,WAAW,CACT,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAAC;IACxB,cAAc,CACZ,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAIhD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAGhF,gBAAgB,IAAI,YAAY,EAAE,CAAC;IAGnC,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC9E,iBAAiB,IAAI,MAAM,CAAC;IAM5B,QAAQ,CACN,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,sBAAsB,GACjC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,cAAc,IAAI,MAAM,CAAC;IAGzB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7B,aAAa,CACX,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAG5C,WAAW,CAAC,CAAC,SAAS,MAAM,qBAAqB,EAC/C,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GACjC,iBAAiB,CAAC;CACtB;AAED,QAAA,MAAM,eAAe,uBACoC,CAAC;AAE1D,eAAe,eAAe,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAiKitModule.js","sourceRoot":"","sources":["../src/ExpoAiKitModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAmFxD,MAAM,eAAe,GACnB,mBAAmB,CAAwB,WAAW,CAAC,CAAC;AAE1D,eAAe,eAAe,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\nimport type { EventSubscription } from 'expo-modules-core';\nimport {\n BuiltInModel,\n DownloadableModelStatus,\n LLMMessage,\n LLMResponse,\n LLMStreamEvent,\n ModelDownloadProgressEvent,\n ModelStateChangeEvent,\n} from './types';\n\nexport type ExpoAiKitModuleEvents = {\n onStreamToken: (event: LLMStreamEvent) => void;\n onDownloadProgress: (event: ModelDownloadProgressEvent) => void;\n onModelStateChange: (event: ModelStateChangeEvent) => void;\n};\n\n/** Generation parameters passed to native. All fields optional; -1 / absent means \"unset\". */\nexport type NativeGenerationConfig = {\n temperature?: number;\n topK?: number;\n topP?: number;\n seed?: number;\n maxTokens?: number;\n};\n\nexport interface ExpoAiKitNativeModule {\n // Existing inference API\n isAvailable(): boolean;\n // sessionId lets stopStreaming() cancel an in-flight (non-streaming) generation too.\n sendMessage(\n messages: LLMMessage[],\n systemPrompt: string,\n sessionId: string\n ): Promise<LLMResponse>;\n startStreaming(\n messages: LLMMessage[],\n systemPrompt: string,\n sessionId: string\n ): Promise<void>;\n // Cancels either a streaming session or a sendMessage session by id.\n stopStreaming(sessionId: string): Promise<void>;\n\n // Model discovery\n getBuiltInModels(): BuiltInModel[];\n // Async: iOS reads actor-isolated state (so it bridges as a Promise); Android\n // returns synchronously. Callers must await — see getDownloadableModels.\n getDownloadableModelStatus(modelId: string): Promise<DownloadableModelStatus>;\n getDeviceRamBytes(): number;\n\n // Model selection & memory management\n // setModel is async: switching to a downloadable model loads it into memory.\n // Auto-unloads the previous downloadable model (only one loaded at a time).\n // `generation` carries best-effort sampling defaults for the session.\n setModel(\n modelId: string,\n minRamBytes: number,\n backend: string,\n generation: NativeGenerationConfig\n ): Promise<void>;\n getActiveModel(): string;\n // Explicitly free memory from the loaded downloadable model.\n // Reverts to the platform built-in model.\n unloadModel(): Promise<void>;\n\n // Model lifecycle (downloadable models only)\n downloadModel(\n modelId: string,\n url: string,\n sha256: string\n ): Promise<void>;\n // Cancels an in-flight download for the given model (no-op if none).\n cancelDownload(modelId: string): Promise<void>;\n deleteModel(modelId: string): Promise<void>;\n\n // Event subscription\n addListener<K extends keyof ExpoAiKitModuleEvents>(\n eventName: K,\n listener: ExpoAiKitModuleEvents[K]\n ): EventSubscription;\n}\n\nconst ExpoAiKitModule =\n requireNativeModule<ExpoAiKitNativeModule>('ExpoAiKit');\n\nexport default ExpoAiKitModule;\n"]}
1
+ {"version":3,"file":"ExpoAiKitModule.js","sourceRoot":"","sources":["../src/ExpoAiKitModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAuFxD,MAAM,eAAe,GACnB,mBAAmB,CAAwB,WAAW,CAAC,CAAC;AAE1D,eAAe,eAAe,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\nimport type { EventSubscription } from 'expo-modules-core';\nimport {\n BuiltInModel,\n DownloadableModelStatus,\n LLMMessage,\n LLMResponse,\n LLMStreamEvent,\n ModelDownloadProgressEvent,\n ModelStateChangeEvent,\n} from './types';\n\nexport type ExpoAiKitModuleEvents = {\n onStreamToken: (event: LLMStreamEvent) => void;\n onDownloadProgress: (event: ModelDownloadProgressEvent) => void;\n onModelStateChange: (event: ModelStateChangeEvent) => void;\n};\n\n/** Generation parameters passed to native. All fields optional; -1 / absent means \"unset\". */\nexport type NativeGenerationConfig = {\n temperature?: number;\n topK?: number;\n topP?: number;\n seed?: number;\n maxTokens?: number;\n};\n\nexport interface ExpoAiKitNativeModule {\n // Existing inference API\n isAvailable(): boolean;\n // sessionId lets stopStreaming() cancel an in-flight (non-streaming) generation too.\n sendMessage(\n messages: LLMMessage[],\n systemPrompt: string,\n sessionId: string\n ): Promise<LLMResponse>;\n startStreaming(\n messages: LLMMessage[],\n systemPrompt: string,\n sessionId: string\n ): Promise<void>;\n // Cancels either a streaming session or a sendMessage session by id.\n stopStreaming(sessionId: string): Promise<void>;\n\n // Embeddings. iOS-only (Apple NLContextualEmbedding); the JS layer guards the\n // platform, so this is never reached on Android/web.\n embed(texts: string[]): Promise<{ embeddings: number[][]; dimensions: number }>;\n\n // Model discovery\n getBuiltInModels(): BuiltInModel[];\n // Async: iOS reads actor-isolated state (so it bridges as a Promise); Android\n // returns synchronously. Callers must await — see getDownloadableModels.\n getDownloadableModelStatus(modelId: string): Promise<DownloadableModelStatus>;\n getDeviceRamBytes(): number;\n\n // Model selection & memory management\n // setModel is async: switching to a downloadable model loads it into memory.\n // Auto-unloads the previous downloadable model (only one loaded at a time).\n // `generation` carries best-effort sampling defaults for the session.\n setModel(\n modelId: string,\n minRamBytes: number,\n backend: string,\n generation: NativeGenerationConfig\n ): Promise<void>;\n getActiveModel(): string;\n // Explicitly free memory from the loaded downloadable model.\n // Reverts to the platform built-in model.\n unloadModel(): Promise<void>;\n\n // Model lifecycle (downloadable models only)\n downloadModel(\n modelId: string,\n url: string,\n sha256: string\n ): Promise<void>;\n // Cancels an in-flight download for the given model (no-op if none).\n cancelDownload(modelId: string): Promise<void>;\n deleteModel(modelId: string): Promise<void>;\n\n // Event subscription\n addListener<K extends keyof ExpoAiKitModuleEvents>(\n eventName: K,\n listener: ExpoAiKitModuleEvents[K]\n ): EventSubscription;\n}\n\nconst ExpoAiKitModule =\n requireNativeModule<ExpoAiKitNativeModule>('ExpoAiKit');\n\nexport default ExpoAiKitModule;\n"]}
@@ -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":"AAOA,OAAO,EAAE,UAAU,EAAkD,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEjF,8EAA8E;AAC9E,iEAAiE;AACjE,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,8EAA8E;AAC9E,+EAA+E;AAC/E,kEAAkE;AAClE,EAAE;AACF,6EAA6E;AAC7E,gEAAgE;AAChE,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAyB;IAC/C,WAAW,EAAE;QACX,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,SAAS;QAClB,SAAS,EAAE,SAAS;QACpB,UAAU,EAAE,SAAS;KACtB;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,SAAS;KACrB;CACF,CAAC;AAEF,IAAI,SAAS,GAAG,CAAC,CAAC;AAClB,uEAAuE;AACvE,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAClD,CAAC;AAcD,2EAA2E;AAC3E,2EAA2E;AAC3E,MAAM,iBAAiB,GAAG;IACxB,aAAa;IACb,MAAM;IACN,MAAM;IACN,MAAM;IACN,iBAAiB;IACjB,eAAe;IACf,iBAAiB;IACjB,kBAAkB;CACV,CAAC;AAEX;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAmC;IACpE,MAAM,QAAQ,GAAsB,EAAE,CAAC;IAEvC,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,OAAO;gBAChB,OAAO,EACL,uGAAuG;aAC1G,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,MAAM,OAAO,GAAY,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1D,IAAI,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAChD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC7B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;oBACnB,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;oBACnC,UAAU,EAAE,aAAa,CAAE,IAAoC,CAAC,WAAW,CAAC;iBAC7E,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,aAAa;oBACnB,OAAO,EAAE,kBAAkB,IAAI,CAAC,EAAE,GAAG;oBACrC,OAAO,EAAE,+EAA+E;iBACzF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAEvC,IAAI,eAAe,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChF,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC3D,eAAe,IAAI,sDAAsD,CAAC;QAC1E,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,wBAAwB;YACjC,OAAO,EAAE,kFAAkF;SAC5F,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC9D,eAAe,IAAI,gDAAgD,UAAU,CAAC,QAAQ,IAAI,CAAC;QAC3F,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,8BAA8B;YACvC,OAAO,EAAE,kFAAkF;SAC5F,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,iBAAiB,GAAG,EAAE,CAAC;IAC3B,IAAI,OAAO,CAAC,cAAc,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5C,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,4DAA4D;YAC5D,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,0DAA0D;aACpE,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,IAAI,CAAC;YAChB,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,MAAM;gBAC/C,CAAC,CAAC,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;gBACtE,CAAC,CAAC,2EAA2E,CAAC;QAClF,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAElC,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACrC,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAClC,MAAM;YACR,CAAC;YACD,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO;qBACzB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;oBACZ,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;wBAAE,OAAO,IAAI,CAAC,IAAI,CAAC;oBAC3C,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnC,CAAC,CAAC;qBACD,IAAI,CAAC,EAAE,CAAC,CAAC;gBACZ,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,MAAM;YACR,CAAC;YACD,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,MAAM,GAAa,EAAE,CAAC;gBAC5B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACnC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;wBACzB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACzB,CAAC;yBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;wBACrC,gEAAgE;wBAChE,6CAA6C;wBAC7C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;oBACpF,CAAC;yBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;wBACrC,gCAAgC;oBAClC,CAAC;yBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;wBACvC,mEAAmE;oBACrE,CAAC;yBAAM,CAAC;wBACN,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACnC,CAAC;gBACH,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC/B,IAAI,IAAI,KAAK,EAAE;oBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrE,MAAM;YACR,CAAC;YACD,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,MAAM,GAAa,EAAE,CAAC;gBAC5B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACnC,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;wBAChC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;oBAC9E,CAAC;oBACD,mEAAmE;oBACnE,+CAA+C;gBACjD,CAAC;gBACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;oBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrF,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAClF,MAAM,MAAM,GAAG,CAAC,GAAG,WAAW,EAAE,GAAG,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAClB,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACrD,CAAC;AAOD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,SAAmB,EACnB,QAAiB;IAEjB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC9C,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC3B,OAAO;gBACL,IAAI,EAAE,WAAW;gBACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;aACxC,CAAC;QACJ,CAAC;QACD,qEAAqE;QACrE,oEAAoE;QACpE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAChC,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,MAAM,CAAC,EAAE;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1E,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,gDAAgD;IACjF,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAChC,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,MAAuC;IACtE,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC,KAAK,CAAC;QACtB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC,KAAK,CAAC;QACtB,KAAK,YAAY;YACf,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,KAAK,YAAY;YACf,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,KAAK,kBAAkB;YACrB,OAAO,EAAE,KAAK,EAAE,wBAAwB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC;QACxF,KAAK,SAAS;YACZ,OAAO,MAAM,CAAC,KAAK;iBAChB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,WAAW,CAAC,CAAC;iBAC5E,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,MAAe;IACpC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,MAAoB,CAAC;IAC/E,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,OAAO,IAAI,UAAU,CACnB,sBAAsB,EACtB,EAAE,EACF,iCAAiC,IAAI,qCAAqC;QACxE,mEAAmE,CACtE,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC","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"}