expo-ai-kit 0.7.0 → 0.9.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 +56 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -2
- package/build/index.js.map +1 -1
- package/build/models.d.ts +74 -1
- package/build/models.d.ts.map +1 -1
- package/build/models.js +222 -2
- package/build/models.js.map +1 -1
- package/build/types.d.ts +2 -0
- package/build/types.d.ts.map +1 -1
- package/build/types.js.map +1 -1
- package/package.json +4 -1
- package/src/index.ts +3 -2
- package/src/models.ts +250 -2
- package/src/types.ts +2 -0
package/README.md
CHANGED
|
@@ -98,12 +98,26 @@ Omit a tool's `execute` to gate it yourself: the loop stops with `finishReason:
|
|
|
98
98
|
and hands you the proposed call to confirm before running. Keep tool sets small and parameter
|
|
99
99
|
schemas flat — on-device models pick tools more reliably that way.
|
|
100
100
|
|
|
101
|
-
## Downloadable
|
|
101
|
+
## Downloadable models
|
|
102
|
+
|
|
103
|
+
Beyond the OS built-ins, you can download open models (via LiteRT-LM) and switch to them at
|
|
104
|
+
runtime — a size ladder from sub-GB to ~4 GB:
|
|
105
|
+
|
|
106
|
+
| id | params | download | license |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `qwen3-0.6b` | 0.6B | ~0.5 GB | Apache-2.0 |
|
|
109
|
+
| `gemma-e2b` | 2.3B | ~2.6 GB | Gemma |
|
|
110
|
+
| `qwen3-1.7b` | 1.7B | ~2.1 GB | Apache-2.0 |
|
|
111
|
+
| `qwen3-4b` | 4B | ~2.7 GB | Apache-2.0 |
|
|
112
|
+
| `gemma-e4b` | 4.5B | ~3.7 GB | Gemma |
|
|
113
|
+
| `phi-4-mini` | 3.8B | ~3.9 GB | MIT |
|
|
102
114
|
|
|
103
115
|
```tsx
|
|
104
|
-
import { getRecommendedModel, downloadModel, setModel } from 'expo-ai-kit';
|
|
116
|
+
import { getDownloadableModels, getRecommendedModel, downloadModel, setModel } from 'expo-ai-kit';
|
|
117
|
+
|
|
118
|
+
await getDownloadableModels(); // full catalog + per-device status, size, and license
|
|
105
119
|
|
|
106
|
-
const best = await getRecommendedModel(); //
|
|
120
|
+
const best = await getRecommendedModel(); // biggest model the device can run, else null
|
|
107
121
|
if (best) {
|
|
108
122
|
await downloadModel(best.id, { onProgress: (p) => console.log(p) });
|
|
109
123
|
await setModel(best.id, { generation: { temperature: 0.7 } });
|
|
@@ -111,11 +125,50 @@ if (best) {
|
|
|
111
125
|
}
|
|
112
126
|
```
|
|
113
127
|
|
|
128
|
+
Each entry carries a `license` — check it before shipping a model to your users.
|
|
129
|
+
|
|
130
|
+
### Bring your own model
|
|
131
|
+
|
|
132
|
+
Not just the built-in list — register any LiteRT-LM model at runtime with `registerModel()`.
|
|
133
|
+
The download is still integrity-checked against the `sha256` you provide. Use
|
|
134
|
+
`fetchModelMetadata()` once at dev time to grab the hash + size from HuggingFace, then **pin**
|
|
135
|
+
them in your code so the integrity guarantee is real and not just corruption-detection.
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { registerModel, fetchModelMetadata, downloadModel, setModel } from 'expo-ai-kit';
|
|
139
|
+
|
|
140
|
+
const url = 'https://huggingface.co/litert-community/Qwen3-4B/resolve/main/qwen3_4b_mixed_int4.litertlm';
|
|
141
|
+
|
|
142
|
+
// Dev time: log these once, then hardcode the returned sha256 below.
|
|
143
|
+
// const { sha256, sizeBytes } = await fetchModelMetadata(url);
|
|
144
|
+
|
|
145
|
+
registerModel({
|
|
146
|
+
id: 'qwen3-4b-custom',
|
|
147
|
+
name: 'Qwen3 4B',
|
|
148
|
+
parameterCount: '4B',
|
|
149
|
+
quantization: 'int4',
|
|
150
|
+
downloadUrl: url,
|
|
151
|
+
sha256: 'f0794bc77efeaaf4f7af815f04c483b19b8f2ae4a102cef1b7b760a25848a18e', // pinned
|
|
152
|
+
sizeBytes: 2_659_057_664,
|
|
153
|
+
contextWindow: 4096,
|
|
154
|
+
minRamBytes: 3_000_000_000,
|
|
155
|
+
supportedPlatforms: ['ios', 'android'],
|
|
156
|
+
license: 'Apache-2.0',
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await downloadModel('qwen3-4b-custom');
|
|
160
|
+
await setModel('qwen3-4b-custom');
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Custom models are in-memory — call `registerModel()` at startup each launch (the downloaded
|
|
164
|
+
file persists on disk, so its `'downloaded'` status survives restarts once re-registered).
|
|
165
|
+
|
|
114
166
|
## API
|
|
115
167
|
|
|
116
168
|
Inference: `isAvailable`, `sendMessage`, `streamMessage`, `generateObject`, `generateText`.
|
|
117
169
|
Models: `getBuiltInModels`, `getDownloadableModels`, `getRecommendedModel`,
|
|
118
170
|
`downloadModel`, `cancelDownload`, `deleteModel`, `setModel`, `unloadModel`, `getActiveModel`.
|
|
171
|
+
Custom models: `registerModel`, `unregisterModel`, `getRegisteredModels`, `fetchModelMetadata`.
|
|
119
172
|
|
|
120
173
|
Failures throw `ModelError` with a typed `.code`. Full TypeScript definitions ship with
|
|
121
174
|
the package — see [the docs](https://expo-ai-kit.dev) for the complete reference.
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,EACX,gBAAgB,EAEhB,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,iBAAiB,EAIjB,eAAe,EACf,UAAU,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAInB,MAAM,SAAS,CAAC;AAiBjB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AA0HzB;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAKpD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,WAAW,CAAC,CAmEtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,CAAC,EAAE,gBAAgB,GACzB,eAAe,CAwFjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,cAAc,CAAC,CAAC,GAAG,OAAO,EAC9C,QAAQ,EAAE,UAAU,EAAE,EACtB,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAoElC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAmJ7B;AAMD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAKhE;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,EACX,gBAAgB,EAEhB,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,iBAAiB,EAIjB,eAAe,EACf,UAAU,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAInB,MAAM,SAAS,CAAC;AAiBjB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AA0HzB;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAKpD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,WAAW,CAAC,CAmEtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,CAAC,EAAE,gBAAgB,GACzB,eAAe,CAwFjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,cAAc,CAAC,CAAC,GAAG,OAAO,EAC9C,QAAQ,EAAE,UAAU,EAAE,EACtB,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAoElC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAmJ7B;AAMD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAKhE;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAkC1E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAM7E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACpD,OAAO,CAAC,IAAI,CAAC,CA+Cf;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnE;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAQxF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjD"}
|
package/build/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Platform } from 'react-native';
|
|
|
3
3
|
import { ModelError, } from './types';
|
|
4
4
|
import { buildSchemaInstruction, buildSchemaRepair, extractJson, validateAgainstSchema, REPAIR_INVALID_JSON, } from './structured';
|
|
5
5
|
import { buildToolInstruction, parseToolCall, buildUnknownToolRepair, buildToolArgsRepair, formatToolResult, } from './tools';
|
|
6
|
-
import {
|
|
6
|
+
import { getAllModels, getRegistryEntry } from './models';
|
|
7
7
|
export * from './types';
|
|
8
8
|
export * from './models';
|
|
9
9
|
const DEFAULT_SYSTEM_PROMPT = 'You are a helpful, friendly assistant. Answer the user directly and concisely.';
|
|
@@ -626,7 +626,7 @@ export async function getDownloadableModels() {
|
|
|
626
626
|
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
627
627
|
return [];
|
|
628
628
|
}
|
|
629
|
-
const platformModels =
|
|
629
|
+
const platformModels = getAllModels().filter((entry) => entry.supportedPlatforms.includes(Platform.OS));
|
|
630
630
|
let deviceRamBytes = 0;
|
|
631
631
|
try {
|
|
632
632
|
deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();
|
|
@@ -642,6 +642,7 @@ export async function getDownloadableModels() {
|
|
|
642
642
|
id: entry.id,
|
|
643
643
|
name: entry.name,
|
|
644
644
|
parameterCount: entry.parameterCount,
|
|
645
|
+
license: entry.license,
|
|
645
646
|
sizeBytes: entry.sizeBytes,
|
|
646
647
|
contextWindow: entry.contextWindow,
|
|
647
648
|
minRamBytes: entry.minRamBytes,
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import ExpoAiKitModule, { type NativeGenerationConfig } from './ExpoAiKitModule';\nimport { Platform } from 'react-native';\nimport {\n LLMMessage,\n LLMSendOptions,\n LLMResponse,\n LLMStreamOptions,\n LLMStreamEvent,\n LLMStreamCallback,\n LLMStreamHandle,\n BuiltInModel,\n DownloadableModel,\n GenerationConfig,\n ModelError,\n ModelErrorCode,\n SetModelOptions,\n JSONSchema,\n GenerateObjectOptions,\n GenerateObjectResult,\n GenerateTextOptions,\n GenerateTextResult,\n ToolCall,\n ToolResult,\n StepResult,\n} from './types';\nimport {\n buildSchemaInstruction,\n buildSchemaRepair,\n extractJson,\n validateAgainstSchema,\n REPAIR_INVALID_JSON,\n} from './structured';\nimport {\n buildToolInstruction,\n parseToolCall,\n buildUnknownToolRepair,\n buildToolArgsRepair,\n formatToolResult,\n} from './tools';\nimport { MODEL_REGISTRY, getRegistryEntry } from './models';\n\nexport * from './types';\nexport * from './models';\n\nconst DEFAULT_SYSTEM_PROMPT =\n 'You are a helpful, friendly assistant. Answer the user directly and concisely.';\n\nconst DEFAULT_OBJECT_SYSTEM_PROMPT =\n 'You output structured data as JSON. Follow the provided JSON Schema exactly.';\n\nlet streamIdCounter = 0;\nfunction generateSessionId(): string {\n return `gen_${Date.now()}_${++streamIdCounter}`;\n}\n\n// The set of codes the native layer encodes in error messages as \"CODE:modelId:reason\".\nconst KNOWN_ERROR_CODES = new Set<ModelErrorCode>([\n 'MODEL_NOT_FOUND',\n 'MODEL_NOT_DOWNLOADED',\n 'DOWNLOAD_FAILED',\n 'DOWNLOAD_CORRUPT',\n 'DOWNLOAD_STORAGE_FULL',\n 'DOWNLOAD_CANCELLED',\n 'INFERENCE_OOM',\n 'INFERENCE_FAILED',\n 'INFERENCE_BUSY',\n 'INFERENCE_CANCELLED',\n 'MODEL_LOAD_FAILED',\n 'DEVICE_NOT_SUPPORTED',\n]);\n\n/**\n * Normalize an error from the native layer into a {@link ModelError}.\n *\n * The native modules format failures as \"CODE:modelId:reason\" (see the\n * GemmaError/GemmaInferenceClient contract). Expo surfaces that string as the\n * error's message, so we parse it here and rethrow a typed ModelError with a\n * reliable `.code` and `.modelId`. Anything unrecognized becomes UNKNOWN.\n */\nfunction toModelError(e: unknown): never {\n if (e instanceof ModelError) throw e;\n const message = String((e as any)?.message ?? e ?? '');\n const match = /^([A-Z_]+):([^:]*):([\\s\\S]*)$/.exec(message);\n if (match && KNOWN_ERROR_CODES.has(match[1] as ModelErrorCode)) {\n throw new ModelError(match[1] as ModelErrorCode, match[2], match[3]);\n }\n throw new ModelError('UNKNOWN', '', message);\n}\n\n/** Run a native promise, normalizing any rejection into a ModelError. */\nasync function wrapNative<T>(run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (e) {\n toModelError(e);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Single-flight inference guard\n// ---------------------------------------------------------------------------\n// On-device models are backed by a single native context + KV cache that is not\n// safe for concurrent decodes (interleaving can corrupt the cache and crash the\n// native side). JS is single-threaded, so a synchronous check-and-set of this\n// flag before any `await` is race-free. The flag is shared by sendMessage and\n// streamMessage and is held until the *native* call settles — not until an\n// early abort — so a detached-but-still-running generation still blocks a new one.\nlet inferenceInFlight = false;\n\nfunction acquireInference(): void {\n if (inferenceInFlight) {\n throw new ModelError(\n 'INFERENCE_BUSY',\n '',\n 'A generation is already in flight. Wait for it to finish, or stop the active stream first.'\n );\n }\n inferenceInFlight = true;\n}\n\n/**\n * Map the public GenerationConfig to the native shape, dropping undefined fields\n * and validating ranges up front so callers get a clear error instead of an\n * opaque native MODEL_LOAD_FAILED from the sampler.\n */\nfunction toNativeGeneration(g?: GenerationConfig): NativeGenerationConfig {\n const out: NativeGenerationConfig = {};\n if (g?.temperature != null) {\n if (g.temperature < 0) {\n throw new Error('generation.temperature must be >= 0');\n }\n out.temperature = g.temperature;\n }\n if (g?.topK != null) {\n if (!Number.isInteger(g.topK) || g.topK <= 0) {\n throw new Error('generation.topK must be a positive integer');\n }\n out.topK = g.topK;\n }\n if (g?.topP != null) {\n if (g.topP < 0 || g.topP > 1) {\n throw new Error('generation.topP must be within [0, 1]');\n }\n out.topP = g.topP;\n }\n if (g?.seed != null) {\n if (!Number.isInteger(g.seed)) {\n throw new Error('generation.seed must be an integer');\n }\n out.seed = g.seed;\n }\n if (g?.maxTokens != null) {\n if (!Number.isInteger(g.maxTokens) || g.maxTokens <= 0) {\n throw new Error('generation.maxTokens must be a positive integer');\n }\n out.maxTokens = g.maxTokens;\n }\n return out;\n}\n\n// ============================================================================\n// Inference API\n// ============================================================================\n\n/**\n * Check if on-device AI is available on the current device.\n * Returns false on unsupported platforms (web, etc.).\n */\nexport async function isAvailable(): Promise<boolean> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return false;\n }\n return ExpoAiKitModule.isAvailable();\n}\n\n/**\n * Send messages to the on-device LLM and get a response.\n *\n * @param messages - Array of messages representing the conversation\n * @param options - Optional settings (systemPrompt fallback)\n * @returns Promise with the generated response\n *\n * @example\n * ```ts\n * const response = await sendMessage([\n * { role: 'user', content: 'What is 2 + 2?' }\n * ]);\n * console.log(response.text); // \"4\"\n * ```\n *\n * @example\n * ```ts\n * // With system prompt\n * const response = await sendMessage(\n * [{ role: 'user', content: 'Hello!' }],\n * { systemPrompt: 'You are a pirate. Respond in pirate speak.' }\n * );\n * ```\n *\n * @example\n * ```ts\n * // Multi-turn conversation\n * const response = await sendMessage([\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'My name is Alice.' },\n * { role: 'assistant', content: 'Nice to meet you, Alice!' },\n * { role: 'user', content: 'What is my name?' }\n * ]);\n * ```\n */\nexport async function sendMessage(\n messages: LLMMessage[],\n options?: LLMSendOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n\n if (options?.signal?.aborted) {\n throw new ModelError('INFERENCE_CANCELLED', '', 'Aborted before start');\n }\n\n // Determine system prompt: use from messages array if present, else options, else default\n const hasSystemMessage = messages.some((m) => m.role === 'system');\n const systemPrompt = hasSystemMessage\n ? '' // Native will extract from messages\n : options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n\n acquireInference(); // throws INFERENCE_BUSY if a generation is already running\n const sessionId = generateSessionId();\n\n // Hold the single-flight flag until the NATIVE call settles — even if the\n // caller aborts early — because the model may keep computing in the background.\n const native = ExpoAiKitModule.sendMessage(messages, systemPrompt, sessionId);\n const release = () => {\n inferenceInFlight = false;\n };\n native.then(release, release);\n\n const signal = options?.signal;\n if (!signal) {\n try {\n return await native;\n } catch (e) {\n toModelError(e);\n }\n }\n\n // Race the native result against the abort signal. On abort we unblock the\n // caller immediately and best-effort ask native to cancel; the flag stays\n // held (via `release` above) until the native call actually finishes.\n return await new Promise<LLMResponse>((resolve, reject) => {\n let done = false;\n const finish = (action: () => void) => {\n if (done) return;\n done = true;\n signal.removeEventListener('abort', onAbort);\n action();\n };\n function onAbort() {\n ExpoAiKitModule.stopStreaming(sessionId).catch(() => {});\n finish(() => reject(new ModelError('INFERENCE_CANCELLED', '', 'Aborted by caller')));\n }\n signal.addEventListener('abort', onAbort);\n native.then(\n (r) => finish(() => resolve(r)),\n (e) =>\n finish(() => {\n try {\n toModelError(e);\n } catch (me) {\n reject(me);\n }\n })\n );\n });\n}\n\n/**\n * Stream messages to the on-device LLM and receive progressive token updates.\n *\n * @param messages - Array of messages representing the conversation\n * @param onToken - Callback function called for each token/chunk received\n * @param options - Optional settings (systemPrompt fallback)\n * @returns Object with stop() function to cancel streaming and promise that resolves when complete\n *\n * @example\n * ```ts\n * // Basic streaming\n * const { promise } = streamMessage(\n * [{ role: 'user', content: 'Tell me a story' }],\n * (event) => {\n * console.log(event.token); // Each token as it arrives\n * console.log(event.accumulatedText); // Full text so far\n * }\n * );\n * await promise;\n * ```\n *\n * @example\n * ```ts\n * // With cancellation\n * const { promise, stop } = streamMessage(\n * [{ role: 'user', content: 'Write a long essay' }],\n * (event) => setText(event.accumulatedText)\n * );\n *\n * // Cancel after 5 seconds\n * setTimeout(() => stop(), 5000);\n * ```\n */\nexport function streamMessage(\n messages: LLMMessage[],\n onToken: LLMStreamCallback,\n options?: LLMStreamOptions\n): LLMStreamHandle {\n // Handle unsupported platforms\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return {\n promise: Promise.resolve({ text: '' }),\n stop: () => {},\n };\n }\n\n if (!messages || messages.length === 0) {\n return {\n promise: Promise.reject(new Error('messages array cannot be empty')),\n stop: () => {},\n };\n }\n\n if (inferenceInFlight) {\n return {\n promise: Promise.reject(\n new ModelError(\n 'INFERENCE_BUSY',\n '',\n 'A generation is already in flight. Stop the active stream first.'\n )\n ),\n stop: () => {},\n };\n }\n inferenceInFlight = true; // set synchronously — race-free with other JS\n\n const sessionId = generateSessionId();\n\n // Determine system prompt: use from messages array if present, else options, else default\n const hasSystemMessage = messages.some((m) => m.role === 'system');\n const systemPrompt = hasSystemMessage\n ? '' // Native will extract from messages\n : options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n\n let finalText = '';\n let settled = false;\n let subscription: ReturnType<typeof ExpoAiKitModule.addListener> | undefined;\n let resolveOuter!: (r: LLMResponse) => void;\n let rejectOuter!: (e: unknown) => void;\n\n // Settle exactly once: remove the listener and release the single-flight flag.\n const settle = (action: () => void) => {\n if (settled) return;\n settled = true;\n subscription?.remove();\n inferenceInFlight = false;\n action();\n };\n\n const promise = new Promise<LLMResponse>((resolve, reject) => {\n resolveOuter = resolve;\n rejectOuter = reject;\n });\n\n subscription = ExpoAiKitModule.addListener(\n 'onStreamToken',\n (event: LLMStreamEvent) => {\n if (event.sessionId !== sessionId) return;\n finalText = event.accumulatedText;\n onToken(event);\n if (event.isDone) settle(() => resolveOuter({ text: finalText }));\n }\n );\n\n ExpoAiKitModule.startStreaming(messages, systemPrompt, sessionId).catch(\n (error) => {\n settle(() => {\n try {\n toModelError(error);\n } catch (me) {\n rejectOuter(me);\n }\n });\n }\n );\n\n const stop = () => {\n // Best-effort native cancel (native also emits a terminal isDone on cancel),\n // but resolve immediately with the text so far so `promise` can never hang.\n ExpoAiKitModule.stopStreaming(sessionId).catch(() => {});\n settle(() => resolveOuter({ text: finalText }));\n };\n\n return { promise, stop };\n}\n\n/**\n * Generate a typed object instead of free text.\n *\n * You describe the shape you want with a JSON Schema. expo-ai-kit appends a\n * strict instruction to the system prompt, runs the on-device model, extracts\n * the JSON from its output (tolerating prose and ```json fences), validates it\n * against the schema, and — on a parse error or schema mismatch — feeds the\n * error back and re-prompts up to `maxRepairAttempts` times.\n *\n * Works on every backend (Apple Foundation Models, ML Kit, Gemma) because it is\n * orchestrated over {@link sendMessage}: it honors the same single-flight guard,\n * `AbortSignal`, and `systemPrompt` semantics. Keep schemas small and shallow —\n * on-device models follow flat shapes far more reliably than deeply nested ones.\n *\n * @param messages - The conversation, same shape as {@link sendMessage}.\n * @param schema - A JSON Schema describing the desired result.\n * @param options - Optional settings (systemPrompt, signal, maxRepairAttempts).\n * @returns `{ object, text }` — the validated value and the raw output.\n * @throws {ModelError} INFERENCE_FAILED if no schema-valid JSON is produced\n * after the repair attempts. Also propagates INFERENCE_BUSY / INFERENCE_CANCELLED\n * from the underlying generation.\n *\n * @example\n * ```ts\n * type Recipe = { title: string; minutes: number; ingredients: string[] };\n *\n * const { object } = await generateObject<Recipe>(\n * [{ role: 'user', content: 'A quick weeknight pasta.' }],\n * {\n * type: 'object',\n * properties: {\n * title: { type: 'string' },\n * minutes: { type: 'integer' },\n * ingredients: { type: 'array', items: { type: 'string' } },\n * },\n * required: ['title', 'minutes', 'ingredients'],\n * },\n * );\n * object.title; // typed Recipe\n * ```\n */\nexport async function generateObject<T = unknown>(\n messages: LLMMessage[],\n schema: JSONSchema,\n options?: GenerateObjectOptions\n): Promise<GenerateObjectResult<T>> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n '',\n 'generateObject is only available on iOS and Android'\n );\n }\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n if (!schema || typeof schema !== 'object') {\n throw new Error('schema must be a JSON Schema object');\n }\n\n const maxRepairAttempts = Math.max(0, options?.maxRepairAttempts ?? 2);\n const instruction = buildSchemaInstruction(schema);\n\n // Inject the schema instruction. If the caller supplied a system message we\n // append to it (sendMessage reads system from the array); otherwise we carry\n // the instruction via the systemPrompt option, which sendMessage applies when\n // the array has no system message — including on the repair turns we append.\n const sysIdx = messages.findIndex((m) => m.role === 'system');\n let working: LLMMessage[];\n let systemPrompt: string | undefined;\n if (sysIdx >= 0) {\n working = messages.map((m, i) =>\n i === sysIdx ? { role: m.role, content: `${m.content}\\n\\n${instruction}` } : m\n );\n systemPrompt = undefined; // the array carries the system message\n } else {\n working = [...messages];\n systemPrompt = `${options?.systemPrompt ?? DEFAULT_OBJECT_SYSTEM_PROMPT}\\n\\n${instruction}`;\n }\n\n let lastText = '';\n for (let attempt = 0; attempt <= maxRepairAttempts; attempt++) {\n const { text } = await sendMessage(working, { systemPrompt, signal: options?.signal });\n lastText = text;\n\n const parsed = extractJson(text);\n if (parsed.ok) {\n const errors = validateAgainstSchema(parsed.value, schema);\n if (errors.length === 0) {\n return { object: parsed.value as T, text };\n }\n if (attempt < maxRepairAttempts) {\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: buildSchemaRepair(errors) },\n ];\n }\n } else if (attempt < maxRepairAttempts) {\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: REPAIR_INVALID_JSON },\n ];\n }\n }\n\n throw new ModelError(\n 'INFERENCE_FAILED',\n getActiveModel(),\n `generateObject: model did not return schema-valid JSON after ${maxRepairAttempts + 1} attempt(s). ` +\n `Last output: ${lastText.slice(0, 200)}`\n );\n}\n\n/**\n * Generate text, optionally letting the model call tools (functions) you provide.\n *\n * Unlike {@link generateObject} (where the JSON *is* the answer), tool calling is\n * a loop: the model proposes a call, expo-ai-kit validates the arguments against\n * the tool's `parameters`, runs your `execute`, feeds the result back, and lets\n * the model continue — until it produces a plain-text answer or the `maxSteps`\n * budget is reached. With no `tools`, this is a single text generation.\n *\n * Orchestrated in JS over {@link sendMessage}, so it works on every backend\n * (Apple Foundation Models, ML Kit, Gemma) and inherits the single-flight guard,\n * `AbortSignal`, and `systemPrompt` semantics. On-device models are imperfect at\n * tool selection, so the loop is defensive: malformed calls, unknown tool names,\n * and schema-invalid arguments are re-prompted up to `maxRepairAttempts` times,\n * and a tool with no `execute` stops the loop and returns the proposed call for\n * you to gate. Keep tool sets small and `parameters` flat for best reliability.\n *\n * @param messages - The conversation, same shape as {@link sendMessage}.\n * @param options - Tools, `maxSteps`, `systemPrompt`, `signal`, `maxRepairAttempts`.\n * @returns `{ text, steps, toolCalls, toolResults, finishReason }`.\n * @throws {ModelError} INFERENCE_FAILED if the model keeps proposing an unknown\n * tool or schema-invalid arguments after the repair attempts. Also propagates\n * INFERENCE_BUSY / INFERENCE_CANCELLED from the underlying generation.\n *\n * @example\n * ```ts\n * const { text } = await generateText(\n * [{ role: 'user', content: 'What should I wear in Paris today?' }],\n * {\n * tools: {\n * getWeather: {\n * description: 'Get the current weather for a city.',\n * parameters: {\n * type: 'object',\n * properties: { city: { type: 'string' } },\n * required: ['city'],\n * },\n * execute: async ({ city }: { city: string }) => fetchWeather(city),\n * },\n * },\n * },\n * );\n * ```\n *\n * @example\n * ```ts\n * // Human-in-the-loop: omit `execute` to gate the call yourself.\n * const res = await generateText(messages, {\n * tools: { deleteAccount: { description: '…', parameters: { type: 'object' } } },\n * });\n * if (res.finishReason === 'tool-calls') {\n * const call = res.toolCalls[0]; // confirm with the user before running\n * }\n * ```\n */\nexport async function generateText(\n messages: LLMMessage[],\n options?: GenerateTextOptions\n): Promise<GenerateTextResult> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n '',\n 'generateText is only available on iOS and Android'\n );\n }\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n\n const tools = options?.tools ?? {};\n const toolNames = Object.keys(tools);\n const maxSteps = Math.max(1, options?.maxSteps ?? 5);\n const maxRepairAttempts = Math.max(0, options?.maxRepairAttempts ?? 2);\n\n // Inject the tool instruction the same way generateObject injects its schema\n // instruction: into the array's system message if present, else via the\n // systemPrompt option. With no tools, this is a plain single-shot generation.\n const instruction = toolNames.length > 0 ? buildToolInstruction(tools) : '';\n const sysIdx = messages.findIndex((m) => m.role === 'system');\n let working: LLMMessage[];\n let systemPrompt: string | undefined;\n if (instruction === '') {\n working = [...messages];\n systemPrompt = options?.systemPrompt;\n } else if (sysIdx >= 0) {\n working = messages.map((m, i) =>\n i === sysIdx ? { role: m.role, content: `${m.content}\\n\\n${instruction}` } : m\n );\n systemPrompt = undefined; // the array carries the system message\n } else {\n working = [...messages];\n systemPrompt = `${options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT}\\n\\n${instruction}`;\n }\n\n const steps: StepResult[] = [];\n const allToolCalls: ToolCall[] = [];\n const allToolResults: ToolResult[] = [];\n\n for (let step = 0; step < maxSteps; step++) {\n // One model round-trip, with an inner repair loop for malformed/invalid calls.\n let call: ToolCall | null = null;\n let text = '';\n\n for (let repair = 0; ; repair++) {\n const r = await sendMessage(working, { systemPrompt, signal: options?.signal });\n text = r.text;\n\n if (toolNames.length === 0) break; // no tools → this is the final answer\n\n const parsed = parseToolCall(text, toolNames);\n if (parsed.kind === 'text') break; // plain answer, no tool call\n\n if (parsed.kind === 'unknown-tool') {\n if (repair >= maxRepairAttempts) {\n throw new ModelError(\n 'INFERENCE_FAILED',\n getActiveModel(),\n `generateText: model called unknown tool \"${parsed.toolName}\" after ${maxRepairAttempts + 1} attempt(s).`\n );\n }\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: buildUnknownToolRepair(parsed.toolName, toolNames) },\n ];\n continue;\n }\n\n // parsed.kind === 'tool' — validate the proposed args before executing.\n const errors = validateAgainstSchema(parsed.args, tools[parsed.toolName].parameters);\n if (errors.length === 0) {\n call = { toolName: parsed.toolName, args: parsed.args };\n break;\n }\n if (repair >= maxRepairAttempts) {\n throw new ModelError(\n 'INFERENCE_FAILED',\n getActiveModel(),\n `generateText: arguments for \"${parsed.toolName}\" failed schema validation after ` +\n `${maxRepairAttempts + 1} attempt(s): ${errors.slice(0, 4).join('; ')}`\n );\n }\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: buildToolArgsRepair(parsed.toolName, errors) },\n ];\n }\n\n // No tool call this step → the model produced its final text answer.\n if (!call) {\n steps.push({ text, toolCalls: [], toolResults: [] });\n return {\n text,\n steps,\n toolCalls: allToolCalls,\n toolResults: allToolResults,\n finishReason: 'stop',\n };\n }\n\n allToolCalls.push(call);\n const tool = tools[call.toolName];\n\n // No execute → hand the proposed call back to the caller (human-in-the-loop).\n if (typeof tool.execute !== 'function') {\n steps.push({ text, toolCalls: [call], toolResults: [] });\n return {\n text,\n steps,\n toolCalls: allToolCalls,\n toolResults: allToolResults,\n finishReason: 'tool-calls',\n };\n }\n\n // Run the tool. A thrown error is fed back as the result so the model can recover.\n let result: unknown;\n try {\n result = await tool.execute(call.args);\n } catch (e) {\n result = { error: String((e as any)?.message ?? e) };\n }\n const toolResult: ToolResult = { toolName: call.toolName, args: call.args, result };\n allToolResults.push(toolResult);\n steps.push({ text, toolCalls: [call], toolResults: [toolResult] });\n\n // Feed the call + result back into the conversation for the next step.\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: formatToolResult(call.toolName, result) },\n ];\n }\n\n // Step budget exhausted while still calling tools — no final answer was\n // produced. Signal it via finishReason so the caller can raise maxSteps.\n return {\n text: '',\n steps,\n toolCalls: allToolCalls,\n toolResults: allToolResults,\n finishReason: 'max-steps',\n };\n}\n\n// ============================================================================\n// Model Management API\n// ============================================================================\n\n/**\n * Get all built-in models available on the current platform.\n *\n * Built-in models are provided by the OS and require no download.\n * On iOS this returns Apple Foundation Models; on Android, ML Kit.\n *\n * @returns Array of built-in models with availability status\n */\nexport async function getBuiltInModels(): Promise<BuiltInModel[]> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return [];\n }\n return ExpoAiKitModule.getBuiltInModels();\n}\n\n/**\n * Get all downloadable models from the registry, enriched with on-device status.\n *\n * Reads from the hardcoded MODEL_REGISTRY and queries the native layer\n * for the current download/load status of each model.\n *\n * @returns Array of downloadable models with their current status\n */\nexport async function getDownloadableModels(): Promise<DownloadableModel[]> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return [];\n }\n\n const platformModels = MODEL_REGISTRY.filter((entry) =>\n entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')\n );\n\n let deviceRamBytes = 0;\n try {\n deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();\n } catch {\n // Native call unavailable -- default to 0 (all models will show meetsRequirements: false)\n }\n\n return Promise.all(\n platformModels.map(async (entry) => {\n // Await: on iOS this bridges as a Promise (reads actor state); on Android\n // it's synchronous and awaiting a plain value is a no-op.\n const status = await ExpoAiKitModule.getDownloadableModelStatus(entry.id);\n return {\n id: entry.id,\n name: entry.name,\n parameterCount: entry.parameterCount,\n sizeBytes: entry.sizeBytes,\n contextWindow: entry.contextWindow,\n minRamBytes: entry.minRamBytes,\n meetsRequirements: deviceRamBytes >= entry.minRamBytes,\n status,\n };\n })\n );\n}\n\n/**\n * Pick the best downloadable model the current device can run.\n *\n * Returns the most capable model (largest, by RAM requirement) whose\n * `meetsRequirements` is true — e.g. Gemma 4 E4B on high-spec phones, falling\n * back to E2B on more constrained ones — or `null` if the device can't run any.\n *\n * This is a convenience over {@link getDownloadableModels}; the caller still\n * downloads + activates explicitly. Pass `platform` is implicit (current OS).\n *\n * @example\n * ```ts\n * const best = await getRecommendedModel();\n * if (best) {\n * await downloadModel(best.id, { onProgress });\n * await setModel(best.id);\n * }\n * ```\n */\nexport async function getRecommendedModel(): Promise<DownloadableModel | null> {\n const models = await getDownloadableModels();\n const runnable = models.filter((m) => m.meetsRequirements);\n if (runnable.length === 0) return null;\n // Higher RAM requirement ⇒ larger/more capable model. Prefer the biggest that fits.\n return runnable.sort((a, b) => b.minRamBytes - a.minRamBytes)[0];\n}\n\n/**\n * Download a model to the device.\n *\n * Looks up the model in the registry, validates platform support and\n * device requirements, then initiates the download with integrity verification.\n *\n * @param modelId - ID of the model to download (e.g. 'gemma-e2b')\n * @param options - Optional download configuration\n * @param options.onProgress - Callback with download progress (0-1)\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is not in the registry\n * @throws {ModelError} DEVICE_NOT_SUPPORTED if platform is not supported\n * @throws {ModelError} DOWNLOAD_FAILED on network error\n * @throws {ModelError} DOWNLOAD_STORAGE_FULL if insufficient disk space\n * @throws {ModelError} DOWNLOAD_CORRUPT if SHA256 hash doesn't match\n */\nexport async function downloadModel(\n modelId: string,\n options?: { onProgress?: (progress: number) => void }\n): Promise<void> {\n const entry = getRegistryEntry(modelId);\n if (!entry) {\n throw new ModelError('MODEL_NOT_FOUND', modelId);\n }\n\n if (!entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')) {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n modelId,\n `Model ${modelId} is not supported on ${Platform.OS}`\n );\n }\n\n try {\n const deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();\n if (deviceRamBytes < entry.minRamBytes) {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n modelId,\n `Device has ${Math.round(deviceRamBytes / 1e9)}GB RAM, model requires ${Math.round(entry.minRamBytes / 1e9)}GB`\n );\n }\n } catch (e) {\n if (e instanceof ModelError) throw e;\n // If getDeviceRamBytes is unavailable, skip the check\n }\n\n let subscription: ReturnType<typeof ExpoAiKitModule.addListener> | undefined;\n if (options?.onProgress) {\n subscription = ExpoAiKitModule.addListener(\n 'onDownloadProgress',\n (event) => {\n if (event.modelId === modelId) {\n options.onProgress!(event.progress);\n }\n }\n );\n }\n\n try {\n await wrapNative(() =>\n ExpoAiKitModule.downloadModel(modelId, entry.downloadUrl, entry.sha256)\n );\n } finally {\n subscription?.remove();\n }\n}\n\n/**\n * Cancel an in-flight download for a model.\n *\n * The in-progress {@link downloadModel} promise rejects with a\n * DOWNLOAD_CANCELLED {@link ModelError}. No-op if the model isn't downloading.\n *\n * @param modelId - ID of the model whose download should be cancelled\n */\nexport async function cancelDownload(modelId: string): Promise<void> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return;\n }\n await wrapNative(() => ExpoAiKitModule.cancelDownload(modelId));\n}\n\n/**\n * Delete a downloaded model from the device.\n *\n * If the model is currently loaded, it will be unloaded first.\n *\n * @param modelId - ID of the model to delete\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is not in the registry\n */\nexport async function deleteModel(modelId: string): Promise<void> {\n const entry = getRegistryEntry(modelId);\n if (!entry) {\n throw new ModelError('MODEL_NOT_FOUND', modelId);\n }\n\n await wrapNative(() => ExpoAiKitModule.deleteModel(modelId));\n}\n\n/**\n * Set the active model for inference.\n *\n * This is the sole gatekeeper for model validity. If setModel succeeds,\n * the model is loaded and ready -- sendMessage never needs its own check.\n *\n * For downloadable models, this loads the model into memory (status\n * transitions: loading -> ready). Only one downloadable model can be\n * loaded at a time; the previous one is auto-unloaded.\n *\n * For built-in models, this simply switches the active backend.\n *\n * If setModel was never called, sendMessage uses the platform built-in\n * model (today's behavior, no error).\n *\n * @param modelId - ID of the model to activate (e.g. 'gemma-e2b', 'apple-fm', 'mlkit')\n * @param options - Optional configuration for model loading\n * @param options.backend - Hardware backend: 'auto' (default, GPU with CPU fallback), 'gpu', or 'cpu'\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is invalid\n * @throws {ModelError} MODEL_NOT_DOWNLOADED if the downloadable model file is not on disk\n * @throws {ModelError} MODEL_LOAD_FAILED if loading into memory fails\n * @throws {ModelError} INFERENCE_OOM if device can't fit model in memory\n */\nexport async function setModel(modelId: string, options?: SetModelOptions): Promise<void> {\n const entry = getRegistryEntry(modelId);\n const minRamBytes = entry?.minRamBytes ?? 0;\n const backend = options?.backend ?? 'auto';\n const generation = toNativeGeneration(options?.generation);\n await wrapNative(() =>\n ExpoAiKitModule.setModel(modelId, minRamBytes, backend, generation)\n );\n}\n\n/**\n * Get the ID of the currently active model.\n *\n * @returns The active model ID (e.g. 'apple-fm', 'mlkit', 'gemma-e2b')\n */\nexport function getActiveModel(): string {\n return ExpoAiKitModule.getActiveModel();\n}\n\n/**\n * Explicitly unload the current downloadable model from memory.\n *\n * Frees memory and reverts to the platform built-in model.\n * No-op if no downloadable model is currently loaded.\n */\nexport async function unloadModel(): Promise<void> {\n await wrapNative(() => ExpoAiKitModule.unloadModel());\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAgD,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAWL,UAAU,GAWX,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,WAAW,EACX,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE1D,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AAEzB,MAAM,qBAAqB,GACzB,gFAAgF,CAAC;AAEnF,MAAM,4BAA4B,GAChC,8EAA8E,CAAC;AAEjF,IAAI,eAAe,GAAG,CAAC,CAAC;AACxB,SAAS,iBAAiB;IACxB,OAAO,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;AAClD,CAAC;AAED,wFAAwF;AACxF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAiB;IAChD,iBAAiB;IACjB,sBAAsB;IACtB,iBAAiB;IACjB,kBAAkB;IAClB,uBAAuB;IACvB,oBAAoB;IACpB,eAAe;IACf,kBAAkB;IAClB,gBAAgB;IAChB,qBAAqB;IACrB,mBAAmB;IACnB,sBAAsB;CACvB,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,CAAU;IAC9B,IAAI,CAAC,YAAY,UAAU;QAAE,MAAM,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,MAAM,CAAE,CAAS,EAAE,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5D,IAAI,KAAK,IAAI,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAmB,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAmB,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,IAAI,UAAU,CAAC,SAAS,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED,yEAAyE;AACzE,KAAK,UAAU,UAAU,CAAI,GAAqB;IAChD,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,YAAY,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,gCAAgC;AAChC,8EAA8E;AAC9E,gFAAgF;AAChF,gFAAgF;AAChF,8EAA8E;AAC9E,8EAA8E;AAC9E,2EAA2E;AAC3E,mFAAmF;AACnF,IAAI,iBAAiB,GAAG,KAAK,CAAC;AAE9B,SAAS,gBAAgB;IACvB,IAAI,iBAAiB,EAAE,CAAC;QACtB,MAAM,IAAI,UAAU,CAClB,gBAAgB,EAChB,EAAE,EACF,4FAA4F,CAC7F,CAAC;IACJ,CAAC;IACD,iBAAiB,GAAG,IAAI,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,CAAoB;IAC9C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,CAAC,EAAE,WAAW,IAAI,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC;IAClC,CAAC;IACD,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QACD,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IACpB,CAAC;IACD,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;QACpB,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QACD,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IACpB,CAAC;IACD,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QACD,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IACpB,CAAC;IACD,IAAI,CAAC,EAAE,SAAS,IAAI,IAAI,EAAE,CAAC;QACzB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACrE,CAAC;QACD,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;IAC9B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,eAAe,CAAC,WAAW,EAAE,CAAC;AACvC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAsB,EACtB,OAAwB;IAExB,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACtB,CAAC;IAED,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,UAAU,CAAC,qBAAqB,EAAE,EAAE,EAAE,sBAAsB,CAAC,CAAC;IAC1E,CAAC;IAED,0FAA0F;IAC1F,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IACnE,MAAM,YAAY,GAAG,gBAAgB;QACnC,CAAC,CAAC,EAAE,CAAC,oCAAoC;QACzC,CAAC,CAAC,OAAO,EAAE,YAAY,IAAI,qBAAqB,CAAC;IAEnD,gBAAgB,EAAE,CAAC,CAAC,2DAA2D;IAC/E,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;IAEtC,0EAA0E;IAC1E,gFAAgF;IAChF,MAAM,MAAM,GAAG,eAAe,CAAC,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;IAC9E,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,iBAAiB,GAAG,KAAK,CAAC;IAC5B,CAAC,CAAC;IACF,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAE9B,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC;IAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,MAAM,MAAM,CAAC;QACtB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,YAAY,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,0EAA0E;IAC1E,sEAAsE;IACtE,OAAO,MAAM,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxD,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,CAAC,MAAkB,EAAE,EAAE;YACpC,IAAI,IAAI;gBAAE,OAAO;YACjB,IAAI,GAAG,IAAI,CAAC;YACZ,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,MAAM,EAAE,CAAC;QACX,CAAC,CAAC;QACF,SAAS,OAAO;YACd,eAAe,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACzD,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,qBAAqB,EAAE,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC;QACvF,CAAC;QACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,CACT,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC/B,CAAC,CAAC,EAAE,EAAE,CACJ,MAAM,CAAC,GAAG,EAAE;YACV,IAAI,CAAC;gBACH,YAAY,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,EAAE,EAAE,CAAC;gBACZ,MAAM,CAAC,EAAE,CAAC,CAAC;YACb,CAAC;QACH,CAAC,CAAC,CACL,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,aAAa,CAC3B,QAAsB,EACtB,OAA0B,EAC1B,OAA0B;IAE1B,+BAA+B;IAC/B,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YACtC,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;SACf,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACpE,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;SACf,CAAC;IACJ,CAAC;IAED,IAAI,iBAAiB,EAAE,CAAC;QACtB,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,MAAM,CACrB,IAAI,UAAU,CACZ,gBAAgB,EAChB,EAAE,EACF,kEAAkE,CACnE,CACF;YACD,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;SACf,CAAC;IACJ,CAAC;IACD,iBAAiB,GAAG,IAAI,CAAC,CAAC,8CAA8C;IAExE,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;IAEtC,0FAA0F;IAC1F,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IACnE,MAAM,YAAY,GAAG,gBAAgB;QACnC,CAAC,CAAC,EAAE,CAAC,oCAAoC;QACzC,CAAC,CAAC,OAAO,EAAE,YAAY,IAAI,qBAAqB,CAAC;IAEnD,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,YAAwE,CAAC;IAC7E,IAAI,YAAuC,CAAC;IAC5C,IAAI,WAAkC,CAAC;IAEvC,+EAA+E;IAC/E,MAAM,MAAM,GAAG,CAAC,MAAkB,EAAE,EAAE;QACpC,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,iBAAiB,GAAG,KAAK,CAAC;QAC1B,MAAM,EAAE,CAAC;IACX,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3D,YAAY,GAAG,OAAO,CAAC;QACvB,WAAW,GAAG,MAAM,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,YAAY,GAAG,eAAe,CAAC,WAAW,CACxC,eAAe,EACf,CAAC,KAAqB,EAAE,EAAE;QACxB,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;YAAE,OAAO;QAC1C,SAAS,GAAG,KAAK,CAAC,eAAe,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,CAAC;QACf,IAAI,KAAK,CAAC,MAAM;YAAE,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC,CACF,CAAC;IAEF,eAAe,CAAC,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,KAAK,CACrE,CAAC,KAAK,EAAE,EAAE;QACR,MAAM,CAAC,GAAG,EAAE;YACV,IAAI,CAAC;gBACH,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;YAAC,OAAO,EAAE,EAAE,CAAC;gBACZ,WAAW,CAAC,EAAE,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CACF,CAAC;IAEF,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,6EAA6E;QAC7E,4EAA4E;QAC5E,eAAe,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACzD,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAsB,EACtB,MAAkB,EAClB,OAA+B;IAE/B,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,MAAM,IAAI,UAAU,CAClB,sBAAsB,EACtB,EAAE,EACF,qDAAqD,CACtD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,IAAI,CAAC,CAAC,CAAC;IACvE,MAAM,WAAW,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAEnD,4EAA4E;IAC5E,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAC9D,IAAI,OAAqB,CAAC;IAC1B,IAAI,YAAgC,CAAC;IACrC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAChB,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC9B,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,OAAO,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAC/E,CAAC;QACF,YAAY,GAAG,SAAS,CAAC,CAAC,uCAAuC;IACnE,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;QACxB,YAAY,GAAG,GAAG,OAAO,EAAE,YAAY,IAAI,4BAA4B,OAAO,WAAW,EAAE,CAAC;IAC9F,CAAC;IAED,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,iBAAiB,EAAE,OAAO,EAAE,EAAE,CAAC;QAC9D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACvF,QAAQ,GAAG,IAAI,CAAC;QAEhB,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC3D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAU,EAAE,IAAI,EAAE,CAAC;YAC7C,CAAC;YACD,IAAI,OAAO,GAAG,iBAAiB,EAAE,CAAC;gBAChC,OAAO,GAAG;oBACR,GAAG,OAAO;oBACV,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE;oBACpC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAAE;iBACrD,CAAC;YACJ,CAAC;QACH,CAAC;aAAM,IAAI,OAAO,GAAG,iBAAiB,EAAE,CAAC;YACvC,OAAO,GAAG;gBACR,GAAG,OAAO;gBACV,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE;gBACpC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE;aAC/C,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,IAAI,UAAU,CAClB,kBAAkB,EAClB,cAAc,EAAE,EAChB,gEAAgE,iBAAiB,GAAG,CAAC,eAAe;QAClG,gBAAgB,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC3C,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAsB,EACtB,OAA6B;IAE7B,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,MAAM,IAAI,UAAU,CAClB,sBAAsB,EACtB,EAAE,EACF,mDAAmD,CACpD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAC;IACrD,MAAM,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,IAAI,CAAC,CAAC,CAAC;IAEvE,6EAA6E;IAC7E,wEAAwE;IACxE,8EAA8E;IAC9E,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAC9D,IAAI,OAAqB,CAAC;IAC1B,IAAI,YAAgC,CAAC;IACrC,IAAI,WAAW,KAAK,EAAE,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;QACxB,YAAY,GAAG,OAAO,EAAE,YAAY,CAAC;IACvC,CAAC;SAAM,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC9B,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,OAAO,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAC/E,CAAC;QACF,YAAY,GAAG,SAAS,CAAC,CAAC,uCAAuC;IACnE,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;QACxB,YAAY,GAAG,GAAG,OAAO,EAAE,YAAY,IAAI,qBAAqB,OAAO,WAAW,EAAE,CAAC;IACvF,CAAC;IAED,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAe,EAAE,CAAC;IACpC,MAAM,cAAc,GAAiB,EAAE,CAAC;IAExC,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;QAC3C,+EAA+E;QAC/E,IAAI,IAAI,GAAoB,IAAI,CAAC;QACjC,IAAI,IAAI,GAAG,EAAE,CAAC;QAEd,KAAK,IAAI,MAAM,GAAG,CAAC,GAAI,MAAM,EAAE,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YAChF,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;YAEd,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,CAAC,sCAAsC;YAEzE,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC9C,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;gBAAE,MAAM,CAAC,6BAA6B;YAEhE,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACnC,IAAI,MAAM,IAAI,iBAAiB,EAAE,CAAC;oBAChC,MAAM,IAAI,UAAU,CAClB,kBAAkB,EAClB,cAAc,EAAE,EAChB,4CAA4C,MAAM,CAAC,QAAQ,WAAW,iBAAiB,GAAG,CAAC,cAAc,CAC1G,CAAC;gBACJ,CAAC;gBACD,OAAO,GAAG;oBACR,GAAG,OAAO;oBACV,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE;oBACpC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE;iBAC9E,CAAC;gBACF,SAAS;YACX,CAAC;YAED,wEAAwE;YACxE,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;YACrF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,IAAI,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;gBACxD,MAAM;YACR,CAAC;YACD,IAAI,MAAM,IAAI,iBAAiB,EAAE,CAAC;gBAChC,MAAM,IAAI,UAAU,CAClB,kBAAkB,EAClB,cAAc,EAAE,EAChB,gCAAgC,MAAM,CAAC,QAAQ,mCAAmC;oBAChF,GAAG,iBAAiB,GAAG,CAAC,gBAAgB,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC1E,CAAC;YACJ,CAAC;YACD,OAAO,GAAG;gBACR,GAAG,OAAO;gBACV,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE;gBACpC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE;aACxE,CAAC;QACJ,CAAC;QAED,qEAAqE;QACrE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;YACrD,OAAO;gBACL,IAAI;gBACJ,KAAK;gBACL,SAAS,EAAE,YAAY;gBACvB,WAAW,EAAE,cAAc;gBAC3B,YAAY,EAAE,MAAM;aACrB,CAAC;QACJ,CAAC;QAED,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAElC,8EAA8E;QAC9E,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACvC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;YACzD,OAAO;gBACL,IAAI;gBACJ,KAAK;gBACL,SAAS,EAAE,YAAY;gBACvB,WAAW,EAAE,cAAc;gBAC3B,YAAY,EAAE,YAAY;aAC3B,CAAC;QACJ,CAAC;QAED,mFAAmF;QACnF,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,GAAG,EAAE,KAAK,EAAE,MAAM,CAAE,CAAS,EAAE,OAAO,IAAI,CAAC,CAAC,EAAE,CAAC;QACvD,CAAC;QACD,MAAM,UAAU,GAAe,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QACpF,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAEnE,uEAAuE;QACvE,OAAO,GAAG;YACR,GAAG,OAAO;YACV,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE;YACpC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE;SACnE,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,yEAAyE;IACzE,OAAO;QACL,IAAI,EAAE,EAAE;QACR,KAAK;QACL,SAAS,EAAE,YAAY;QACvB,WAAW,EAAE,cAAc;QAC3B,YAAY,EAAE,WAAW;KAC1B,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,uBAAuB;AACvB,+EAA+E;AAE/E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,eAAe,CAAC,gBAAgB,EAAE,CAAC;AAC5C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,cAAc,GAAG,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CACrD,KAAK,CAAC,kBAAkB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAuB,CAAC,CACpE,CAAC;IAEF,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,CAAC;QACH,cAAc,GAAG,eAAe,CAAC,iBAAiB,EAAE,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,0FAA0F;IAC5F,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAChB,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACjC,0EAA0E;QAC1E,0DAA0D;QAC1D,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,0BAA0B,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1E,OAAO;YACL,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,iBAAiB,EAAE,cAAc,IAAI,KAAK,CAAC,WAAW;YACtD,MAAM;SACP,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC3D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,oFAAoF;IACpF,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAe,EACf,OAAqD;IAErD,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,UAAU,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAuB,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,UAAU,CAClB,sBAAsB,EACtB,OAAO,EACP,SAAS,OAAO,wBAAwB,QAAQ,CAAC,EAAE,EAAE,CACtD,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,cAAc,GAAG,eAAe,CAAC,iBAAiB,EAAE,CAAC;QAC3D,IAAI,cAAc,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;YACvC,MAAM,IAAI,UAAU,CAClB,sBAAsB,EACtB,OAAO,EACP,cAAc,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,GAAG,CAAC,0BAA0B,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC,IAAI,CAChH,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,UAAU;YAAE,MAAM,CAAC,CAAC;QACrC,sDAAsD;IACxD,CAAC;IAED,IAAI,YAAwE,CAAC;IAC7E,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC;QACxB,YAAY,GAAG,eAAe,CAAC,WAAW,CACxC,oBAAoB,EACpB,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,KAAK,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;gBAC9B,OAAO,CAAC,UAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACtC,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,GAAG,EAAE,CACpB,eAAe,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CACxE,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,YAAY,EAAE,MAAM,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,OAAO;IACT,CAAC;IACD,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe;IAC/C,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,UAAU,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAe,EAAE,OAAyB;IACvE,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,KAAK,EAAE,WAAW,IAAI,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,MAAM,CAAC;IAC3C,MAAM,UAAU,GAAG,kBAAkB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC3D,MAAM,UAAU,CAAC,GAAG,EAAE,CACpB,eAAe,CAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,CACpE,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,eAAe,CAAC,cAAc,EAAE,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC,CAAC;AACxD,CAAC","sourcesContent":["import ExpoAiKitModule, { type NativeGenerationConfig } from './ExpoAiKitModule';\nimport { Platform } from 'react-native';\nimport {\n LLMMessage,\n LLMSendOptions,\n LLMResponse,\n LLMStreamOptions,\n LLMStreamEvent,\n LLMStreamCallback,\n LLMStreamHandle,\n BuiltInModel,\n DownloadableModel,\n GenerationConfig,\n ModelError,\n ModelErrorCode,\n SetModelOptions,\n JSONSchema,\n GenerateObjectOptions,\n GenerateObjectResult,\n GenerateTextOptions,\n GenerateTextResult,\n ToolCall,\n ToolResult,\n StepResult,\n} from './types';\nimport {\n buildSchemaInstruction,\n buildSchemaRepair,\n extractJson,\n validateAgainstSchema,\n REPAIR_INVALID_JSON,\n} from './structured';\nimport {\n buildToolInstruction,\n parseToolCall,\n buildUnknownToolRepair,\n buildToolArgsRepair,\n formatToolResult,\n} from './tools';\nimport { getAllModels, getRegistryEntry } from './models';\n\nexport * from './types';\nexport * from './models';\n\nconst DEFAULT_SYSTEM_PROMPT =\n 'You are a helpful, friendly assistant. Answer the user directly and concisely.';\n\nconst DEFAULT_OBJECT_SYSTEM_PROMPT =\n 'You output structured data as JSON. Follow the provided JSON Schema exactly.';\n\nlet streamIdCounter = 0;\nfunction generateSessionId(): string {\n return `gen_${Date.now()}_${++streamIdCounter}`;\n}\n\n// The set of codes the native layer encodes in error messages as \"CODE:modelId:reason\".\nconst KNOWN_ERROR_CODES = new Set<ModelErrorCode>([\n 'MODEL_NOT_FOUND',\n 'MODEL_NOT_DOWNLOADED',\n 'DOWNLOAD_FAILED',\n 'DOWNLOAD_CORRUPT',\n 'DOWNLOAD_STORAGE_FULL',\n 'DOWNLOAD_CANCELLED',\n 'INFERENCE_OOM',\n 'INFERENCE_FAILED',\n 'INFERENCE_BUSY',\n 'INFERENCE_CANCELLED',\n 'MODEL_LOAD_FAILED',\n 'DEVICE_NOT_SUPPORTED',\n]);\n\n/**\n * Normalize an error from the native layer into a {@link ModelError}.\n *\n * The native modules format failures as \"CODE:modelId:reason\" (see the\n * GemmaError/GemmaInferenceClient contract). Expo surfaces that string as the\n * error's message, so we parse it here and rethrow a typed ModelError with a\n * reliable `.code` and `.modelId`. Anything unrecognized becomes UNKNOWN.\n */\nfunction toModelError(e: unknown): never {\n if (e instanceof ModelError) throw e;\n const message = String((e as any)?.message ?? e ?? '');\n const match = /^([A-Z_]+):([^:]*):([\\s\\S]*)$/.exec(message);\n if (match && KNOWN_ERROR_CODES.has(match[1] as ModelErrorCode)) {\n throw new ModelError(match[1] as ModelErrorCode, match[2], match[3]);\n }\n throw new ModelError('UNKNOWN', '', message);\n}\n\n/** Run a native promise, normalizing any rejection into a ModelError. */\nasync function wrapNative<T>(run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (e) {\n toModelError(e);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Single-flight inference guard\n// ---------------------------------------------------------------------------\n// On-device models are backed by a single native context + KV cache that is not\n// safe for concurrent decodes (interleaving can corrupt the cache and crash the\n// native side). JS is single-threaded, so a synchronous check-and-set of this\n// flag before any `await` is race-free. The flag is shared by sendMessage and\n// streamMessage and is held until the *native* call settles — not until an\n// early abort — so a detached-but-still-running generation still blocks a new one.\nlet inferenceInFlight = false;\n\nfunction acquireInference(): void {\n if (inferenceInFlight) {\n throw new ModelError(\n 'INFERENCE_BUSY',\n '',\n 'A generation is already in flight. Wait for it to finish, or stop the active stream first.'\n );\n }\n inferenceInFlight = true;\n}\n\n/**\n * Map the public GenerationConfig to the native shape, dropping undefined fields\n * and validating ranges up front so callers get a clear error instead of an\n * opaque native MODEL_LOAD_FAILED from the sampler.\n */\nfunction toNativeGeneration(g?: GenerationConfig): NativeGenerationConfig {\n const out: NativeGenerationConfig = {};\n if (g?.temperature != null) {\n if (g.temperature < 0) {\n throw new Error('generation.temperature must be >= 0');\n }\n out.temperature = g.temperature;\n }\n if (g?.topK != null) {\n if (!Number.isInteger(g.topK) || g.topK <= 0) {\n throw new Error('generation.topK must be a positive integer');\n }\n out.topK = g.topK;\n }\n if (g?.topP != null) {\n if (g.topP < 0 || g.topP > 1) {\n throw new Error('generation.topP must be within [0, 1]');\n }\n out.topP = g.topP;\n }\n if (g?.seed != null) {\n if (!Number.isInteger(g.seed)) {\n throw new Error('generation.seed must be an integer');\n }\n out.seed = g.seed;\n }\n if (g?.maxTokens != null) {\n if (!Number.isInteger(g.maxTokens) || g.maxTokens <= 0) {\n throw new Error('generation.maxTokens must be a positive integer');\n }\n out.maxTokens = g.maxTokens;\n }\n return out;\n}\n\n// ============================================================================\n// Inference API\n// ============================================================================\n\n/**\n * Check if on-device AI is available on the current device.\n * Returns false on unsupported platforms (web, etc.).\n */\nexport async function isAvailable(): Promise<boolean> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return false;\n }\n return ExpoAiKitModule.isAvailable();\n}\n\n/**\n * Send messages to the on-device LLM and get a response.\n *\n * @param messages - Array of messages representing the conversation\n * @param options - Optional settings (systemPrompt fallback)\n * @returns Promise with the generated response\n *\n * @example\n * ```ts\n * const response = await sendMessage([\n * { role: 'user', content: 'What is 2 + 2?' }\n * ]);\n * console.log(response.text); // \"4\"\n * ```\n *\n * @example\n * ```ts\n * // With system prompt\n * const response = await sendMessage(\n * [{ role: 'user', content: 'Hello!' }],\n * { systemPrompt: 'You are a pirate. Respond in pirate speak.' }\n * );\n * ```\n *\n * @example\n * ```ts\n * // Multi-turn conversation\n * const response = await sendMessage([\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'My name is Alice.' },\n * { role: 'assistant', content: 'Nice to meet you, Alice!' },\n * { role: 'user', content: 'What is my name?' }\n * ]);\n * ```\n */\nexport async function sendMessage(\n messages: LLMMessage[],\n options?: LLMSendOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n\n if (options?.signal?.aborted) {\n throw new ModelError('INFERENCE_CANCELLED', '', 'Aborted before start');\n }\n\n // Determine system prompt: use from messages array if present, else options, else default\n const hasSystemMessage = messages.some((m) => m.role === 'system');\n const systemPrompt = hasSystemMessage\n ? '' // Native will extract from messages\n : options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n\n acquireInference(); // throws INFERENCE_BUSY if a generation is already running\n const sessionId = generateSessionId();\n\n // Hold the single-flight flag until the NATIVE call settles — even if the\n // caller aborts early — because the model may keep computing in the background.\n const native = ExpoAiKitModule.sendMessage(messages, systemPrompt, sessionId);\n const release = () => {\n inferenceInFlight = false;\n };\n native.then(release, release);\n\n const signal = options?.signal;\n if (!signal) {\n try {\n return await native;\n } catch (e) {\n toModelError(e);\n }\n }\n\n // Race the native result against the abort signal. On abort we unblock the\n // caller immediately and best-effort ask native to cancel; the flag stays\n // held (via `release` above) until the native call actually finishes.\n return await new Promise<LLMResponse>((resolve, reject) => {\n let done = false;\n const finish = (action: () => void) => {\n if (done) return;\n done = true;\n signal.removeEventListener('abort', onAbort);\n action();\n };\n function onAbort() {\n ExpoAiKitModule.stopStreaming(sessionId).catch(() => {});\n finish(() => reject(new ModelError('INFERENCE_CANCELLED', '', 'Aborted by caller')));\n }\n signal.addEventListener('abort', onAbort);\n native.then(\n (r) => finish(() => resolve(r)),\n (e) =>\n finish(() => {\n try {\n toModelError(e);\n } catch (me) {\n reject(me);\n }\n })\n );\n });\n}\n\n/**\n * Stream messages to the on-device LLM and receive progressive token updates.\n *\n * @param messages - Array of messages representing the conversation\n * @param onToken - Callback function called for each token/chunk received\n * @param options - Optional settings (systemPrompt fallback)\n * @returns Object with stop() function to cancel streaming and promise that resolves when complete\n *\n * @example\n * ```ts\n * // Basic streaming\n * const { promise } = streamMessage(\n * [{ role: 'user', content: 'Tell me a story' }],\n * (event) => {\n * console.log(event.token); // Each token as it arrives\n * console.log(event.accumulatedText); // Full text so far\n * }\n * );\n * await promise;\n * ```\n *\n * @example\n * ```ts\n * // With cancellation\n * const { promise, stop } = streamMessage(\n * [{ role: 'user', content: 'Write a long essay' }],\n * (event) => setText(event.accumulatedText)\n * );\n *\n * // Cancel after 5 seconds\n * setTimeout(() => stop(), 5000);\n * ```\n */\nexport function streamMessage(\n messages: LLMMessage[],\n onToken: LLMStreamCallback,\n options?: LLMStreamOptions\n): LLMStreamHandle {\n // Handle unsupported platforms\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return {\n promise: Promise.resolve({ text: '' }),\n stop: () => {},\n };\n }\n\n if (!messages || messages.length === 0) {\n return {\n promise: Promise.reject(new Error('messages array cannot be empty')),\n stop: () => {},\n };\n }\n\n if (inferenceInFlight) {\n return {\n promise: Promise.reject(\n new ModelError(\n 'INFERENCE_BUSY',\n '',\n 'A generation is already in flight. Stop the active stream first.'\n )\n ),\n stop: () => {},\n };\n }\n inferenceInFlight = true; // set synchronously — race-free with other JS\n\n const sessionId = generateSessionId();\n\n // Determine system prompt: use from messages array if present, else options, else default\n const hasSystemMessage = messages.some((m) => m.role === 'system');\n const systemPrompt = hasSystemMessage\n ? '' // Native will extract from messages\n : options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n\n let finalText = '';\n let settled = false;\n let subscription: ReturnType<typeof ExpoAiKitModule.addListener> | undefined;\n let resolveOuter!: (r: LLMResponse) => void;\n let rejectOuter!: (e: unknown) => void;\n\n // Settle exactly once: remove the listener and release the single-flight flag.\n const settle = (action: () => void) => {\n if (settled) return;\n settled = true;\n subscription?.remove();\n inferenceInFlight = false;\n action();\n };\n\n const promise = new Promise<LLMResponse>((resolve, reject) => {\n resolveOuter = resolve;\n rejectOuter = reject;\n });\n\n subscription = ExpoAiKitModule.addListener(\n 'onStreamToken',\n (event: LLMStreamEvent) => {\n if (event.sessionId !== sessionId) return;\n finalText = event.accumulatedText;\n onToken(event);\n if (event.isDone) settle(() => resolveOuter({ text: finalText }));\n }\n );\n\n ExpoAiKitModule.startStreaming(messages, systemPrompt, sessionId).catch(\n (error) => {\n settle(() => {\n try {\n toModelError(error);\n } catch (me) {\n rejectOuter(me);\n }\n });\n }\n );\n\n const stop = () => {\n // Best-effort native cancel (native also emits a terminal isDone on cancel),\n // but resolve immediately with the text so far so `promise` can never hang.\n ExpoAiKitModule.stopStreaming(sessionId).catch(() => {});\n settle(() => resolveOuter({ text: finalText }));\n };\n\n return { promise, stop };\n}\n\n/**\n * Generate a typed object instead of free text.\n *\n * You describe the shape you want with a JSON Schema. expo-ai-kit appends a\n * strict instruction to the system prompt, runs the on-device model, extracts\n * the JSON from its output (tolerating prose and ```json fences), validates it\n * against the schema, and — on a parse error or schema mismatch — feeds the\n * error back and re-prompts up to `maxRepairAttempts` times.\n *\n * Works on every backend (Apple Foundation Models, ML Kit, Gemma) because it is\n * orchestrated over {@link sendMessage}: it honors the same single-flight guard,\n * `AbortSignal`, and `systemPrompt` semantics. Keep schemas small and shallow —\n * on-device models follow flat shapes far more reliably than deeply nested ones.\n *\n * @param messages - The conversation, same shape as {@link sendMessage}.\n * @param schema - A JSON Schema describing the desired result.\n * @param options - Optional settings (systemPrompt, signal, maxRepairAttempts).\n * @returns `{ object, text }` — the validated value and the raw output.\n * @throws {ModelError} INFERENCE_FAILED if no schema-valid JSON is produced\n * after the repair attempts. Also propagates INFERENCE_BUSY / INFERENCE_CANCELLED\n * from the underlying generation.\n *\n * @example\n * ```ts\n * type Recipe = { title: string; minutes: number; ingredients: string[] };\n *\n * const { object } = await generateObject<Recipe>(\n * [{ role: 'user', content: 'A quick weeknight pasta.' }],\n * {\n * type: 'object',\n * properties: {\n * title: { type: 'string' },\n * minutes: { type: 'integer' },\n * ingredients: { type: 'array', items: { type: 'string' } },\n * },\n * required: ['title', 'minutes', 'ingredients'],\n * },\n * );\n * object.title; // typed Recipe\n * ```\n */\nexport async function generateObject<T = unknown>(\n messages: LLMMessage[],\n schema: JSONSchema,\n options?: GenerateObjectOptions\n): Promise<GenerateObjectResult<T>> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n '',\n 'generateObject is only available on iOS and Android'\n );\n }\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n if (!schema || typeof schema !== 'object') {\n throw new Error('schema must be a JSON Schema object');\n }\n\n const maxRepairAttempts = Math.max(0, options?.maxRepairAttempts ?? 2);\n const instruction = buildSchemaInstruction(schema);\n\n // Inject the schema instruction. If the caller supplied a system message we\n // append to it (sendMessage reads system from the array); otherwise we carry\n // the instruction via the systemPrompt option, which sendMessage applies when\n // the array has no system message — including on the repair turns we append.\n const sysIdx = messages.findIndex((m) => m.role === 'system');\n let working: LLMMessage[];\n let systemPrompt: string | undefined;\n if (sysIdx >= 0) {\n working = messages.map((m, i) =>\n i === sysIdx ? { role: m.role, content: `${m.content}\\n\\n${instruction}` } : m\n );\n systemPrompt = undefined; // the array carries the system message\n } else {\n working = [...messages];\n systemPrompt = `${options?.systemPrompt ?? DEFAULT_OBJECT_SYSTEM_PROMPT}\\n\\n${instruction}`;\n }\n\n let lastText = '';\n for (let attempt = 0; attempt <= maxRepairAttempts; attempt++) {\n const { text } = await sendMessage(working, { systemPrompt, signal: options?.signal });\n lastText = text;\n\n const parsed = extractJson(text);\n if (parsed.ok) {\n const errors = validateAgainstSchema(parsed.value, schema);\n if (errors.length === 0) {\n return { object: parsed.value as T, text };\n }\n if (attempt < maxRepairAttempts) {\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: buildSchemaRepair(errors) },\n ];\n }\n } else if (attempt < maxRepairAttempts) {\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: REPAIR_INVALID_JSON },\n ];\n }\n }\n\n throw new ModelError(\n 'INFERENCE_FAILED',\n getActiveModel(),\n `generateObject: model did not return schema-valid JSON after ${maxRepairAttempts + 1} attempt(s). ` +\n `Last output: ${lastText.slice(0, 200)}`\n );\n}\n\n/**\n * Generate text, optionally letting the model call tools (functions) you provide.\n *\n * Unlike {@link generateObject} (where the JSON *is* the answer), tool calling is\n * a loop: the model proposes a call, expo-ai-kit validates the arguments against\n * the tool's `parameters`, runs your `execute`, feeds the result back, and lets\n * the model continue — until it produces a plain-text answer or the `maxSteps`\n * budget is reached. With no `tools`, this is a single text generation.\n *\n * Orchestrated in JS over {@link sendMessage}, so it works on every backend\n * (Apple Foundation Models, ML Kit, Gemma) and inherits the single-flight guard,\n * `AbortSignal`, and `systemPrompt` semantics. On-device models are imperfect at\n * tool selection, so the loop is defensive: malformed calls, unknown tool names,\n * and schema-invalid arguments are re-prompted up to `maxRepairAttempts` times,\n * and a tool with no `execute` stops the loop and returns the proposed call for\n * you to gate. Keep tool sets small and `parameters` flat for best reliability.\n *\n * @param messages - The conversation, same shape as {@link sendMessage}.\n * @param options - Tools, `maxSteps`, `systemPrompt`, `signal`, `maxRepairAttempts`.\n * @returns `{ text, steps, toolCalls, toolResults, finishReason }`.\n * @throws {ModelError} INFERENCE_FAILED if the model keeps proposing an unknown\n * tool or schema-invalid arguments after the repair attempts. Also propagates\n * INFERENCE_BUSY / INFERENCE_CANCELLED from the underlying generation.\n *\n * @example\n * ```ts\n * const { text } = await generateText(\n * [{ role: 'user', content: 'What should I wear in Paris today?' }],\n * {\n * tools: {\n * getWeather: {\n * description: 'Get the current weather for a city.',\n * parameters: {\n * type: 'object',\n * properties: { city: { type: 'string' } },\n * required: ['city'],\n * },\n * execute: async ({ city }: { city: string }) => fetchWeather(city),\n * },\n * },\n * },\n * );\n * ```\n *\n * @example\n * ```ts\n * // Human-in-the-loop: omit `execute` to gate the call yourself.\n * const res = await generateText(messages, {\n * tools: { deleteAccount: { description: '…', parameters: { type: 'object' } } },\n * });\n * if (res.finishReason === 'tool-calls') {\n * const call = res.toolCalls[0]; // confirm with the user before running\n * }\n * ```\n */\nexport async function generateText(\n messages: LLMMessage[],\n options?: GenerateTextOptions\n): Promise<GenerateTextResult> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n '',\n 'generateText is only available on iOS and Android'\n );\n }\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n\n const tools = options?.tools ?? {};\n const toolNames = Object.keys(tools);\n const maxSteps = Math.max(1, options?.maxSteps ?? 5);\n const maxRepairAttempts = Math.max(0, options?.maxRepairAttempts ?? 2);\n\n // Inject the tool instruction the same way generateObject injects its schema\n // instruction: into the array's system message if present, else via the\n // systemPrompt option. With no tools, this is a plain single-shot generation.\n const instruction = toolNames.length > 0 ? buildToolInstruction(tools) : '';\n const sysIdx = messages.findIndex((m) => m.role === 'system');\n let working: LLMMessage[];\n let systemPrompt: string | undefined;\n if (instruction === '') {\n working = [...messages];\n systemPrompt = options?.systemPrompt;\n } else if (sysIdx >= 0) {\n working = messages.map((m, i) =>\n i === sysIdx ? { role: m.role, content: `${m.content}\\n\\n${instruction}` } : m\n );\n systemPrompt = undefined; // the array carries the system message\n } else {\n working = [...messages];\n systemPrompt = `${options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT}\\n\\n${instruction}`;\n }\n\n const steps: StepResult[] = [];\n const allToolCalls: ToolCall[] = [];\n const allToolResults: ToolResult[] = [];\n\n for (let step = 0; step < maxSteps; step++) {\n // One model round-trip, with an inner repair loop for malformed/invalid calls.\n let call: ToolCall | null = null;\n let text = '';\n\n for (let repair = 0; ; repair++) {\n const r = await sendMessage(working, { systemPrompt, signal: options?.signal });\n text = r.text;\n\n if (toolNames.length === 0) break; // no tools → this is the final answer\n\n const parsed = parseToolCall(text, toolNames);\n if (parsed.kind === 'text') break; // plain answer, no tool call\n\n if (parsed.kind === 'unknown-tool') {\n if (repair >= maxRepairAttempts) {\n throw new ModelError(\n 'INFERENCE_FAILED',\n getActiveModel(),\n `generateText: model called unknown tool \"${parsed.toolName}\" after ${maxRepairAttempts + 1} attempt(s).`\n );\n }\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: buildUnknownToolRepair(parsed.toolName, toolNames) },\n ];\n continue;\n }\n\n // parsed.kind === 'tool' — validate the proposed args before executing.\n const errors = validateAgainstSchema(parsed.args, tools[parsed.toolName].parameters);\n if (errors.length === 0) {\n call = { toolName: parsed.toolName, args: parsed.args };\n break;\n }\n if (repair >= maxRepairAttempts) {\n throw new ModelError(\n 'INFERENCE_FAILED',\n getActiveModel(),\n `generateText: arguments for \"${parsed.toolName}\" failed schema validation after ` +\n `${maxRepairAttempts + 1} attempt(s): ${errors.slice(0, 4).join('; ')}`\n );\n }\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: buildToolArgsRepair(parsed.toolName, errors) },\n ];\n }\n\n // No tool call this step → the model produced its final text answer.\n if (!call) {\n steps.push({ text, toolCalls: [], toolResults: [] });\n return {\n text,\n steps,\n toolCalls: allToolCalls,\n toolResults: allToolResults,\n finishReason: 'stop',\n };\n }\n\n allToolCalls.push(call);\n const tool = tools[call.toolName];\n\n // No execute → hand the proposed call back to the caller (human-in-the-loop).\n if (typeof tool.execute !== 'function') {\n steps.push({ text, toolCalls: [call], toolResults: [] });\n return {\n text,\n steps,\n toolCalls: allToolCalls,\n toolResults: allToolResults,\n finishReason: 'tool-calls',\n };\n }\n\n // Run the tool. A thrown error is fed back as the result so the model can recover.\n let result: unknown;\n try {\n result = await tool.execute(call.args);\n } catch (e) {\n result = { error: String((e as any)?.message ?? e) };\n }\n const toolResult: ToolResult = { toolName: call.toolName, args: call.args, result };\n allToolResults.push(toolResult);\n steps.push({ text, toolCalls: [call], toolResults: [toolResult] });\n\n // Feed the call + result back into the conversation for the next step.\n working = [\n ...working,\n { role: 'assistant', content: text },\n { role: 'user', content: formatToolResult(call.toolName, result) },\n ];\n }\n\n // Step budget exhausted while still calling tools — no final answer was\n // produced. Signal it via finishReason so the caller can raise maxSteps.\n return {\n text: '',\n steps,\n toolCalls: allToolCalls,\n toolResults: allToolResults,\n finishReason: 'max-steps',\n };\n}\n\n// ============================================================================\n// Model Management API\n// ============================================================================\n\n/**\n * Get all built-in models available on the current platform.\n *\n * Built-in models are provided by the OS and require no download.\n * On iOS this returns Apple Foundation Models; on Android, ML Kit.\n *\n * @returns Array of built-in models with availability status\n */\nexport async function getBuiltInModels(): Promise<BuiltInModel[]> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return [];\n }\n return ExpoAiKitModule.getBuiltInModels();\n}\n\n/**\n * Get all downloadable models from the registry, enriched with on-device status.\n *\n * Reads from the hardcoded MODEL_REGISTRY and queries the native layer\n * for the current download/load status of each model.\n *\n * @returns Array of downloadable models with their current status\n */\nexport async function getDownloadableModels(): Promise<DownloadableModel[]> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return [];\n }\n\n const platformModels = getAllModels().filter((entry) =>\n entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')\n );\n\n let deviceRamBytes = 0;\n try {\n deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();\n } catch {\n // Native call unavailable -- default to 0 (all models will show meetsRequirements: false)\n }\n\n return Promise.all(\n platformModels.map(async (entry) => {\n // Await: on iOS this bridges as a Promise (reads actor state); on Android\n // it's synchronous and awaiting a plain value is a no-op.\n const status = await ExpoAiKitModule.getDownloadableModelStatus(entry.id);\n return {\n id: entry.id,\n name: entry.name,\n parameterCount: entry.parameterCount,\n license: entry.license,\n sizeBytes: entry.sizeBytes,\n contextWindow: entry.contextWindow,\n minRamBytes: entry.minRamBytes,\n meetsRequirements: deviceRamBytes >= entry.minRamBytes,\n status,\n };\n })\n );\n}\n\n/**\n * Pick the best downloadable model the current device can run.\n *\n * Returns the most capable model (largest, by RAM requirement) whose\n * `meetsRequirements` is true — e.g. Gemma 4 E4B on high-spec phones, falling\n * back to E2B on more constrained ones — or `null` if the device can't run any.\n *\n * This is a convenience over {@link getDownloadableModels}; the caller still\n * downloads + activates explicitly. Pass `platform` is implicit (current OS).\n *\n * @example\n * ```ts\n * const best = await getRecommendedModel();\n * if (best) {\n * await downloadModel(best.id, { onProgress });\n * await setModel(best.id);\n * }\n * ```\n */\nexport async function getRecommendedModel(): Promise<DownloadableModel | null> {\n const models = await getDownloadableModels();\n const runnable = models.filter((m) => m.meetsRequirements);\n if (runnable.length === 0) return null;\n // Higher RAM requirement ⇒ larger/more capable model. Prefer the biggest that fits.\n return runnable.sort((a, b) => b.minRamBytes - a.minRamBytes)[0];\n}\n\n/**\n * Download a model to the device.\n *\n * Looks up the model in the registry, validates platform support and\n * device requirements, then initiates the download with integrity verification.\n *\n * @param modelId - ID of the model to download (e.g. 'gemma-e2b')\n * @param options - Optional download configuration\n * @param options.onProgress - Callback with download progress (0-1)\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is not in the registry\n * @throws {ModelError} DEVICE_NOT_SUPPORTED if platform is not supported\n * @throws {ModelError} DOWNLOAD_FAILED on network error\n * @throws {ModelError} DOWNLOAD_STORAGE_FULL if insufficient disk space\n * @throws {ModelError} DOWNLOAD_CORRUPT if SHA256 hash doesn't match\n */\nexport async function downloadModel(\n modelId: string,\n options?: { onProgress?: (progress: number) => void }\n): Promise<void> {\n const entry = getRegistryEntry(modelId);\n if (!entry) {\n throw new ModelError('MODEL_NOT_FOUND', modelId);\n }\n\n if (!entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')) {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n modelId,\n `Model ${modelId} is not supported on ${Platform.OS}`\n );\n }\n\n try {\n const deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();\n if (deviceRamBytes < entry.minRamBytes) {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n modelId,\n `Device has ${Math.round(deviceRamBytes / 1e9)}GB RAM, model requires ${Math.round(entry.minRamBytes / 1e9)}GB`\n );\n }\n } catch (e) {\n if (e instanceof ModelError) throw e;\n // If getDeviceRamBytes is unavailable, skip the check\n }\n\n let subscription: ReturnType<typeof ExpoAiKitModule.addListener> | undefined;\n if (options?.onProgress) {\n subscription = ExpoAiKitModule.addListener(\n 'onDownloadProgress',\n (event) => {\n if (event.modelId === modelId) {\n options.onProgress!(event.progress);\n }\n }\n );\n }\n\n try {\n await wrapNative(() =>\n ExpoAiKitModule.downloadModel(modelId, entry.downloadUrl, entry.sha256)\n );\n } finally {\n subscription?.remove();\n }\n}\n\n/**\n * Cancel an in-flight download for a model.\n *\n * The in-progress {@link downloadModel} promise rejects with a\n * DOWNLOAD_CANCELLED {@link ModelError}. No-op if the model isn't downloading.\n *\n * @param modelId - ID of the model whose download should be cancelled\n */\nexport async function cancelDownload(modelId: string): Promise<void> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return;\n }\n await wrapNative(() => ExpoAiKitModule.cancelDownload(modelId));\n}\n\n/**\n * Delete a downloaded model from the device.\n *\n * If the model is currently loaded, it will be unloaded first.\n *\n * @param modelId - ID of the model to delete\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is not in the registry\n */\nexport async function deleteModel(modelId: string): Promise<void> {\n const entry = getRegistryEntry(modelId);\n if (!entry) {\n throw new ModelError('MODEL_NOT_FOUND', modelId);\n }\n\n await wrapNative(() => ExpoAiKitModule.deleteModel(modelId));\n}\n\n/**\n * Set the active model for inference.\n *\n * This is the sole gatekeeper for model validity. If setModel succeeds,\n * the model is loaded and ready -- sendMessage never needs its own check.\n *\n * For downloadable models, this loads the model into memory (status\n * transitions: loading -> ready). Only one downloadable model can be\n * loaded at a time; the previous one is auto-unloaded.\n *\n * For built-in models, this simply switches the active backend.\n *\n * If setModel was never called, sendMessage uses the platform built-in\n * model (today's behavior, no error).\n *\n * @param modelId - ID of the model to activate (e.g. 'gemma-e2b', 'apple-fm', 'mlkit')\n * @param options - Optional configuration for model loading\n * @param options.backend - Hardware backend: 'auto' (default, GPU with CPU fallback), 'gpu', or 'cpu'\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is invalid\n * @throws {ModelError} MODEL_NOT_DOWNLOADED if the downloadable model file is not on disk\n * @throws {ModelError} MODEL_LOAD_FAILED if loading into memory fails\n * @throws {ModelError} INFERENCE_OOM if device can't fit model in memory\n */\nexport async function setModel(modelId: string, options?: SetModelOptions): Promise<void> {\n const entry = getRegistryEntry(modelId);\n const minRamBytes = entry?.minRamBytes ?? 0;\n const backend = options?.backend ?? 'auto';\n const generation = toNativeGeneration(options?.generation);\n await wrapNative(() =>\n ExpoAiKitModule.setModel(modelId, minRamBytes, backend, generation)\n );\n}\n\n/**\n * Get the ID of the currently active model.\n *\n * @returns The active model ID (e.g. 'apple-fm', 'mlkit', 'gemma-e2b')\n */\nexport function getActiveModel(): string {\n return ExpoAiKitModule.getActiveModel();\n}\n\n/**\n * Explicitly unload the current downloadable model from memory.\n *\n * Frees memory and reverts to the platform built-in model.\n * No-op if no downloadable model is currently loaded.\n */\nexport async function unloadModel(): Promise<void> {\n await wrapNative(() => ExpoAiKitModule.unloadModel());\n}\n\n"]}
|
package/build/models.d.ts
CHANGED
|
@@ -32,11 +32,84 @@ export type ModelRegistryEntry = {
|
|
|
32
32
|
minRamBytes: number;
|
|
33
33
|
/** Platforms this model can run on */
|
|
34
34
|
supportedPlatforms: ('ios' | 'android')[];
|
|
35
|
+
/**
|
|
36
|
+
* License the model weights are distributed under — an SPDX identifier
|
|
37
|
+
* (e.g. 'Apache-2.0', 'MIT') or a family name for non-OSI terms (e.g. 'Gemma',
|
|
38
|
+
* 'Llama-3.2'). Surfaced on {@link DownloadableModel} so app developers can
|
|
39
|
+
* check their obligations before shipping a model to users.
|
|
40
|
+
*/
|
|
41
|
+
license: string;
|
|
35
42
|
};
|
|
36
43
|
export declare const MODEL_REGISTRY: ModelRegistryEntry[];
|
|
37
44
|
/**
|
|
38
|
-
*
|
|
45
|
+
* Validate a model entry, returning a list of human-readable problems
|
|
46
|
+
* (empty ⇒ valid). Pure — used by {@link registerModel} and unit-tested.
|
|
47
|
+
*/
|
|
48
|
+
export declare function validateModelEntry(entry: ModelRegistryEntry): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Register a custom downloadable model at runtime.
|
|
51
|
+
*
|
|
52
|
+
* After registering, the id works with `downloadModel` / `setModel` /
|
|
53
|
+
* `getDownloadableModels` exactly like a built-in. The download is integrity-
|
|
54
|
+
* checked against the `sha256` you provide — pin a value you trust (see
|
|
55
|
+
* {@link fetchModelMetadata}). Throws if the entry is invalid or the id
|
|
56
|
+
* collides with a built-in (curated or native) model.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const { sha256, sizeBytes } = await fetchModelMetadata(url); // dev-time
|
|
61
|
+
* registerModel({
|
|
62
|
+
* id: 'qwen3-8b', name: 'Qwen3 8B', parameterCount: '8B', quantization: 'int4',
|
|
63
|
+
* downloadUrl: url, sha256, sizeBytes,
|
|
64
|
+
* contextWindow: 4096, minRamBytes: 6_000_000_000,
|
|
65
|
+
* supportedPlatforms: ['ios', 'android'], license: 'Apache-2.0',
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare function registerModel(entry: ModelRegistryEntry): void;
|
|
70
|
+
/**
|
|
71
|
+
* Remove a previously {@link registerModel}'d custom model.
|
|
72
|
+
* Returns true if one was removed. Does not delete any downloaded file —
|
|
73
|
+
* use `deleteModel` for that. No-op for built-in models.
|
|
74
|
+
*/
|
|
75
|
+
export declare function unregisterModel(modelId: string): boolean;
|
|
76
|
+
/** All custom models registered via {@link registerModel}, in registration order. */
|
|
77
|
+
export declare function getRegisteredModels(): ModelRegistryEntry[];
|
|
78
|
+
/** Built-in registry plus all custom models. */
|
|
79
|
+
export declare function getAllModels(): ModelRegistryEntry[];
|
|
80
|
+
/**
|
|
81
|
+
* Look up a model registry entry by ID (built-in or custom).
|
|
39
82
|
* Returns undefined if not found.
|
|
40
83
|
*/
|
|
41
84
|
export declare function getRegistryEntry(modelId: string): ModelRegistryEntry | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Parse a HuggingFace "resolve" download URL into its parts.
|
|
87
|
+
* Returns null if the URL isn't a HuggingFace resolve URL. Pure — unit-tested.
|
|
88
|
+
*
|
|
89
|
+
* e.g. https://huggingface.co/litert-community/Qwen3-0.6B/resolve/main/model.litertlm
|
|
90
|
+
* → { repo: 'litert-community/Qwen3-0.6B', revision: 'main', path: 'model.litertlm' }
|
|
91
|
+
*/
|
|
92
|
+
export declare function parseHuggingFaceUrl(url: string): {
|
|
93
|
+
repo: string;
|
|
94
|
+
revision: string;
|
|
95
|
+
path: string;
|
|
96
|
+
} | null;
|
|
97
|
+
/**
|
|
98
|
+
* Look up a model file's SHA256 and byte size from HuggingFace, so you can fill
|
|
99
|
+
* in a {@link registerModel} entry without computing them by hand.
|
|
100
|
+
*
|
|
101
|
+
* Trust note: this reads the hash from the same host you'll download from, so it
|
|
102
|
+
* only guards against download corruption — NOT a maliciously changed upstream
|
|
103
|
+
* repo. For a real supply-chain guarantee, run this once at dev time and PIN the
|
|
104
|
+
* returned `sha256` in your source, exactly like the built-in registry.
|
|
105
|
+
*
|
|
106
|
+
* @param downloadUrl - A HuggingFace resolve URL (the one you'll register).
|
|
107
|
+
* @returns `{ sha256, sizeBytes }` ready to spread into a registry entry.
|
|
108
|
+
* @throws if the URL isn't a HuggingFace resolve URL, the API call fails, or the
|
|
109
|
+
* file isn't an LFS object (no hash/size available).
|
|
110
|
+
*/
|
|
111
|
+
export declare function fetchModelMetadata(downloadUrl: string): Promise<{
|
|
112
|
+
sha256: string;
|
|
113
|
+
sizeBytes: number;
|
|
114
|
+
}>;
|
|
42
115
|
//# sourceMappingURL=models.d.ts.map
|
package/build/models.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,kBAAkB,EAAE,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,kBAAkB,EAAE,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,CAAC;IAC1C;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,kBAAkB,EA6F9C,CAAC;AAqBF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,EAAE,CA+BtE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAU7D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,IAAI,kBAAkB,EAAE,CAE1D;AAED,gDAAgD;AAChD,wBAAgB,YAAY,IAAI,kBAAkB,EAAE,CAEnD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAEhF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,GACV;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMzD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAmChD"}
|
package/build/models.js
CHANGED
|
@@ -19,6 +19,7 @@ export const MODEL_REGISTRY = [
|
|
|
19
19
|
contextWindow: 8_000,
|
|
20
20
|
minRamBytes: 2_000_000_000, // 2GB — LiteRT-LM memory-maps weights, actual RSS ~1.5GB
|
|
21
21
|
supportedPlatforms: ['ios', 'android'],
|
|
22
|
+
license: 'Gemma',
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
id: 'gemma-e4b',
|
|
@@ -31,13 +32,232 @@ export const MODEL_REGISTRY = [
|
|
|
31
32
|
contextWindow: 16_000,
|
|
32
33
|
minRamBytes: 3_000_000_000, // 3GB — LiteRT-LM memory-maps weights
|
|
33
34
|
supportedPlatforms: ['ios', 'android'],
|
|
35
|
+
license: 'Gemma',
|
|
36
|
+
},
|
|
37
|
+
// --- Qwen3 (Apache-2.0) — official litert-community builds. A size ladder
|
|
38
|
+
// from a sub-GB model that runs anywhere up to a 4B that rivals Gemma E4B. ---
|
|
39
|
+
{
|
|
40
|
+
id: 'qwen3-0.6b',
|
|
41
|
+
name: 'Qwen3 0.6B',
|
|
42
|
+
parameterCount: '0.6B',
|
|
43
|
+
quantization: 'mixed-int4',
|
|
44
|
+
downloadUrl: 'https://huggingface.co/litert-community/Qwen3-0.6B/resolve/main/qwen3_0_6b_mixed_int4.litertlm',
|
|
45
|
+
sha256: 'b1baab462f6be49d70eada79d715c2c52cd9ece0cad00bddf6a2c097d23498e9',
|
|
46
|
+
sizeBytes: 497_664_000, // 475MB (exact, HF LFS)
|
|
47
|
+
// The int4 build ships a 2048-token KV. TODO: benchmark on device.
|
|
48
|
+
contextWindow: 2_048,
|
|
49
|
+
minRamBytes: 1_000_000_000, // 1GB — tiny; runs on virtually any modern device
|
|
50
|
+
supportedPlatforms: ['ios', 'android'],
|
|
51
|
+
license: 'Apache-2.0',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'qwen3-1.7b',
|
|
55
|
+
name: 'Qwen3 1.7B',
|
|
56
|
+
parameterCount: '1.7B',
|
|
57
|
+
quantization: 'dynamic-int8',
|
|
58
|
+
downloadUrl: 'https://huggingface.co/litert-community/Qwen3-1.7B/resolve/main/Qwen3_1.7B.litertlm',
|
|
59
|
+
sha256: '66064a4e9269cb693e124c4e3040bcb8a446b10bca42663896329495add3861c',
|
|
60
|
+
sizeBytes: 2_056_729_520, // 2.06GB (exact, HF LFS)
|
|
61
|
+
// Conservative default (not marked in the filename). TODO: benchmark on device.
|
|
62
|
+
contextWindow: 4_096,
|
|
63
|
+
minRamBytes: 2_000_000_000, // 2GB
|
|
64
|
+
supportedPlatforms: ['ios', 'android'],
|
|
65
|
+
license: 'Apache-2.0',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'qwen3-4b',
|
|
69
|
+
name: 'Qwen3 4B',
|
|
70
|
+
parameterCount: '4B',
|
|
71
|
+
quantization: 'mixed-int4',
|
|
72
|
+
downloadUrl: 'https://huggingface.co/litert-community/Qwen3-4B/resolve/main/qwen3_4b_mixed_int4.litertlm',
|
|
73
|
+
sha256: 'f0794bc77efeaaf4f7af815f04c483b19b8f2ae4a102cef1b7b760a25848a18e',
|
|
74
|
+
sizeBytes: 2_659_057_664, // 2.66GB (exact, HF LFS)
|
|
75
|
+
// Conservative default (not marked in the filename). TODO: benchmark on device.
|
|
76
|
+
contextWindow: 4_096,
|
|
77
|
+
minRamBytes: 3_000_000_000, // 3GB — 4B params need more headroom than the similarly-sized E2B
|
|
78
|
+
supportedPlatforms: ['ios', 'android'],
|
|
79
|
+
license: 'Apache-2.0',
|
|
80
|
+
},
|
|
81
|
+
// --- Phi-4 Mini (MIT) — strong reasoning; q8 build, the heaviest downloadable. ---
|
|
82
|
+
{
|
|
83
|
+
id: 'phi-4-mini',
|
|
84
|
+
name: 'Phi-4 Mini',
|
|
85
|
+
parameterCount: '3.8B',
|
|
86
|
+
quantization: 'int8',
|
|
87
|
+
downloadUrl: 'https://huggingface.co/litert-community/Phi-4-mini-instruct/resolve/main/Phi-4-mini-instruct_multi-prefill-seq_q8_ekv4096.litertlm',
|
|
88
|
+
sha256: '7764d4deb53800578307be33039476b38a6c370fff71bedb3c0552563e23ab02',
|
|
89
|
+
sizeBytes: 3_910_090_752, // 3.91GB (exact, HF LFS)
|
|
90
|
+
contextWindow: 4_096, // ekv4096 build
|
|
91
|
+
minRamBytes: 4_000_000_000, // 4GB — q8 weights, heaviest downloadable
|
|
92
|
+
supportedPlatforms: ['ios', 'android'],
|
|
93
|
+
license: 'MIT',
|
|
34
94
|
},
|
|
35
95
|
];
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Custom (developer-registered) models — "bring your own model".
|
|
98
|
+
//
|
|
99
|
+
// The built-in MODEL_REGISTRY above is curated: each entry's SHA256 is pinned by
|
|
100
|
+
// a maintainer who verified the bytes. registerModel() lets app developers add
|
|
101
|
+
// any LiteRT-LM model under the same contract — they supply the metadata
|
|
102
|
+
// (including the SHA256), so the integrity check still holds end-to-end. Custom
|
|
103
|
+
// entries live in memory only; call registerModel() at startup on every launch
|
|
104
|
+
// (the downloaded file on disk persists and is keyed by id, so status survives
|
|
105
|
+
// restarts once you re-register).
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
const customModels = new Map();
|
|
108
|
+
/** Ids owned by the native built-in backends; not valid for custom models. */
|
|
109
|
+
const RESERVED_MODEL_IDS = new Set(['apple-fm', 'mlkit']);
|
|
110
|
+
const SHA256_RE = /^[a-f0-9]{64}$/i;
|
|
111
|
+
/**
|
|
112
|
+
* Validate a model entry, returning a list of human-readable problems
|
|
113
|
+
* (empty ⇒ valid). Pure — used by {@link registerModel} and unit-tested.
|
|
114
|
+
*/
|
|
115
|
+
export function validateModelEntry(entry) {
|
|
116
|
+
const errors = [];
|
|
117
|
+
if (!entry || typeof entry !== 'object')
|
|
118
|
+
return ['entry must be an object'];
|
|
119
|
+
if (!entry.id || typeof entry.id !== 'string')
|
|
120
|
+
errors.push('id is required');
|
|
121
|
+
if (!entry.name || typeof entry.name !== 'string')
|
|
122
|
+
errors.push('name is required');
|
|
123
|
+
if (!entry.parameterCount)
|
|
124
|
+
errors.push('parameterCount is required (e.g. "1.7B")');
|
|
125
|
+
if (!entry.quantization)
|
|
126
|
+
errors.push('quantization is required (e.g. "int4")');
|
|
127
|
+
if (typeof entry.downloadUrl !== 'string' || !/^https?:\/\//.test(entry.downloadUrl)) {
|
|
128
|
+
errors.push('downloadUrl must be an http(s) URL');
|
|
129
|
+
}
|
|
130
|
+
if (typeof entry.sha256 !== 'string' || !SHA256_RE.test(entry.sha256)) {
|
|
131
|
+
errors.push('sha256 must be a 64-character hex string (use fetchModelMetadata to obtain it)');
|
|
132
|
+
}
|
|
133
|
+
if (!Number.isFinite(entry.sizeBytes) || entry.sizeBytes <= 0) {
|
|
134
|
+
errors.push('sizeBytes must be a positive number');
|
|
135
|
+
}
|
|
136
|
+
if (!Number.isFinite(entry.minRamBytes) || entry.minRamBytes < 0) {
|
|
137
|
+
errors.push('minRamBytes must be >= 0');
|
|
138
|
+
}
|
|
139
|
+
if (!Number.isInteger(entry.contextWindow) || entry.contextWindow <= 0) {
|
|
140
|
+
errors.push('contextWindow must be a positive integer');
|
|
141
|
+
}
|
|
142
|
+
if (!Array.isArray(entry.supportedPlatforms) || entry.supportedPlatforms.length === 0) {
|
|
143
|
+
errors.push('supportedPlatforms must list at least one of "ios" / "android"');
|
|
144
|
+
}
|
|
145
|
+
else if (!entry.supportedPlatforms.every((p) => p === 'ios' || p === 'android')) {
|
|
146
|
+
errors.push('supportedPlatforms may only contain "ios" or "android"');
|
|
147
|
+
}
|
|
148
|
+
if (!entry.license || typeof entry.license !== 'string') {
|
|
149
|
+
errors.push('license is required (e.g. "Apache-2.0", "MIT")');
|
|
150
|
+
}
|
|
151
|
+
return errors;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Register a custom downloadable model at runtime.
|
|
155
|
+
*
|
|
156
|
+
* After registering, the id works with `downloadModel` / `setModel` /
|
|
157
|
+
* `getDownloadableModels` exactly like a built-in. The download is integrity-
|
|
158
|
+
* checked against the `sha256` you provide — pin a value you trust (see
|
|
159
|
+
* {@link fetchModelMetadata}). Throws if the entry is invalid or the id
|
|
160
|
+
* collides with a built-in (curated or native) model.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* const { sha256, sizeBytes } = await fetchModelMetadata(url); // dev-time
|
|
165
|
+
* registerModel({
|
|
166
|
+
* id: 'qwen3-8b', name: 'Qwen3 8B', parameterCount: '8B', quantization: 'int4',
|
|
167
|
+
* downloadUrl: url, sha256, sizeBytes,
|
|
168
|
+
* contextWindow: 4096, minRamBytes: 6_000_000_000,
|
|
169
|
+
* supportedPlatforms: ['ios', 'android'], license: 'Apache-2.0',
|
|
170
|
+
* });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export function registerModel(entry) {
|
|
174
|
+
const errors = validateModelEntry(entry);
|
|
175
|
+
if (errors.length > 0) {
|
|
176
|
+
throw new Error(`registerModel: invalid model entry — ${errors.join('; ')}`);
|
|
177
|
+
}
|
|
178
|
+
if (RESERVED_MODEL_IDS.has(entry.id) || MODEL_REGISTRY.some((m) => m.id === entry.id)) {
|
|
179
|
+
throw new Error(`registerModel: "${entry.id}" is a built-in model id; choose a different id`);
|
|
180
|
+
}
|
|
181
|
+
// Clone so later external mutation of the caller's object can't corrupt the registry.
|
|
182
|
+
customModels.set(entry.id, { ...entry, supportedPlatforms: [...entry.supportedPlatforms] });
|
|
183
|
+
}
|
|
36
184
|
/**
|
|
37
|
-
*
|
|
185
|
+
* Remove a previously {@link registerModel}'d custom model.
|
|
186
|
+
* Returns true if one was removed. Does not delete any downloaded file —
|
|
187
|
+
* use `deleteModel` for that. No-op for built-in models.
|
|
188
|
+
*/
|
|
189
|
+
export function unregisterModel(modelId) {
|
|
190
|
+
return customModels.delete(modelId);
|
|
191
|
+
}
|
|
192
|
+
/** All custom models registered via {@link registerModel}, in registration order. */
|
|
193
|
+
export function getRegisteredModels() {
|
|
194
|
+
return [...customModels.values()];
|
|
195
|
+
}
|
|
196
|
+
/** Built-in registry plus all custom models. */
|
|
197
|
+
export function getAllModels() {
|
|
198
|
+
return [...MODEL_REGISTRY, ...customModels.values()];
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Look up a model registry entry by ID (built-in or custom).
|
|
38
202
|
* Returns undefined if not found.
|
|
39
203
|
*/
|
|
40
204
|
export function getRegistryEntry(modelId) {
|
|
41
|
-
return MODEL_REGISTRY.find((m) => m.id === modelId);
|
|
205
|
+
return MODEL_REGISTRY.find((m) => m.id === modelId) ?? customModels.get(modelId);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Parse a HuggingFace "resolve" download URL into its parts.
|
|
209
|
+
* Returns null if the URL isn't a HuggingFace resolve URL. Pure — unit-tested.
|
|
210
|
+
*
|
|
211
|
+
* e.g. https://huggingface.co/litert-community/Qwen3-0.6B/resolve/main/model.litertlm
|
|
212
|
+
* → { repo: 'litert-community/Qwen3-0.6B', revision: 'main', path: 'model.litertlm' }
|
|
213
|
+
*/
|
|
214
|
+
export function parseHuggingFaceUrl(url) {
|
|
215
|
+
if (typeof url !== 'string')
|
|
216
|
+
return null;
|
|
217
|
+
const clean = url.split('#')[0].split('?')[0];
|
|
218
|
+
const m = /^https?:\/\/huggingface\.co\/([^/]+\/[^/]+)\/resolve\/([^/]+)\/(.+)$/.exec(clean);
|
|
219
|
+
if (!m)
|
|
220
|
+
return null;
|
|
221
|
+
return { repo: m[1], revision: m[2], path: decodeURIComponent(m[3]) };
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Look up a model file's SHA256 and byte size from HuggingFace, so you can fill
|
|
225
|
+
* in a {@link registerModel} entry without computing them by hand.
|
|
226
|
+
*
|
|
227
|
+
* Trust note: this reads the hash from the same host you'll download from, so it
|
|
228
|
+
* only guards against download corruption — NOT a maliciously changed upstream
|
|
229
|
+
* repo. For a real supply-chain guarantee, run this once at dev time and PIN the
|
|
230
|
+
* returned `sha256` in your source, exactly like the built-in registry.
|
|
231
|
+
*
|
|
232
|
+
* @param downloadUrl - A HuggingFace resolve URL (the one you'll register).
|
|
233
|
+
* @returns `{ sha256, sizeBytes }` ready to spread into a registry entry.
|
|
234
|
+
* @throws if the URL isn't a HuggingFace resolve URL, the API call fails, or the
|
|
235
|
+
* file isn't an LFS object (no hash/size available).
|
|
236
|
+
*/
|
|
237
|
+
export async function fetchModelMetadata(downloadUrl) {
|
|
238
|
+
const parsed = parseHuggingFaceUrl(downloadUrl);
|
|
239
|
+
if (!parsed) {
|
|
240
|
+
throw new Error('fetchModelMetadata: expected a HuggingFace resolve URL ' +
|
|
241
|
+
'(https://huggingface.co/<owner>/<repo>/resolve/<revision>/<file>)');
|
|
242
|
+
}
|
|
243
|
+
const { repo, revision, path } = parsed;
|
|
244
|
+
const slash = path.lastIndexOf('/');
|
|
245
|
+
const dir = slash >= 0 ? path.slice(0, slash) : '';
|
|
246
|
+
const treeUrl = `https://huggingface.co/api/models/${repo}/tree/${revision}` + (dir ? `/${dir}` : '');
|
|
247
|
+
const res = await fetch(treeUrl);
|
|
248
|
+
if (!res.ok) {
|
|
249
|
+
throw new Error(`fetchModelMetadata: HuggingFace API returned ${res.status} for ${treeUrl}`);
|
|
250
|
+
}
|
|
251
|
+
const items = (await res.json());
|
|
252
|
+
const file = Array.isArray(items) ? items.find((it) => it?.path === path) : undefined;
|
|
253
|
+
if (!file) {
|
|
254
|
+
throw new Error(`fetchModelMetadata: "${path}" not found in ${repo}@${revision}`);
|
|
255
|
+
}
|
|
256
|
+
const sha256 = file.lfs?.oid;
|
|
257
|
+
const sizeBytes = file.lfs?.size ?? file.size;
|
|
258
|
+
if (!sha256 || !sizeBytes) {
|
|
259
|
+
throw new Error(`fetchModelMetadata: "${path}" has no LFS hash/size — is it the actual model weight file?`);
|
|
260
|
+
}
|
|
261
|
+
return { sha256, sizeBytes };
|
|
42
262
|
}
|
|
43
263
|
//# sourceMappingURL=models.js.map
|
package/build/models.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+BH,MAAM,CAAC,MAAM,cAAc,GAAyB;IAClD;QACE,EAAE,EAAE,WAAW;QACf,IAAI,EAAE,aAAa;QACnB,cAAc,EAAE,MAAM;QACtB,YAAY,EAAE,iBAAiB;QAC/B,WAAW,EACT,uGAAuG;QACzG,MAAM,EAAE,kEAAkE;QAC1E,SAAS,EAAE,aAAa,EAAE,yBAAyB;QACnD,0CAA0C;QAC1C,0CAA0C;QAC1C,aAAa,EAAE,KAAK;QACpB,WAAW,EAAE,aAAa,EAAE,yDAAyD;QACrF,kBAAkB,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC;KACvC;IACD;QACE,EAAE,EAAE,WAAW;QACf,IAAI,EAAE,aAAa;QACnB,cAAc,EAAE,MAAM;QACtB,YAAY,EAAE,eAAe;QAC7B,WAAW,EACT,uGAAuG;QACzG,MAAM,EAAE,kEAAkE;QAC1E,SAAS,EAAE,aAAa,EAAE,yBAAyB;QACnD,aAAa,EAAE,MAAM;QACrB,WAAW,EAAE,aAAa,EAAE,sCAAsC;QAClE,kBAAkB,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC;KACvC;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;AACtD,CAAC","sourcesContent":["/**\n * Model Registry\n *\n * Defines all downloadable models known to expo-ai-kit.\n * getDownloadableModels() reads from this registry and enriches\n * each entry with on-device status from the native layer.\n */\n\nexport type ModelRegistryEntry = {\n /** Unique model identifier used in setModel/downloadModel */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Parameter count label */\n parameterCount: string;\n /** Quantization variant */\n quantization: string;\n /** URL to download the LiteRT-LM model file */\n downloadUrl: string;\n /** SHA256 hash for integrity verification after download */\n sha256: string;\n /** Download file size in bytes */\n sizeBytes: number;\n /**\n * Practical context window (max tokens) for this model on constrained devices.\n *\n * These are conservative defaults, NOT the base model's theoretical max.\n * These values should be benchmarked and adjusted during testing with\n * real devices.\n */\n contextWindow: number;\n /** Minimum device RAM in bytes required to run this model */\n minRamBytes: number;\n /** Platforms this model can run on */\n supportedPlatforms: ('ios' | 'android')[];\n};\n\nexport const MODEL_REGISTRY: ModelRegistryEntry[] = [\n {\n id: 'gemma-e2b',\n name: 'Gemma 4 E2B',\n parameterCount: '2.3B',\n quantization: 'mixed-2/4/8-bit',\n downloadUrl:\n 'https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it.litertlm',\n sha256: '181938105e0eefd105961417e8da75903eacda102c4fce9ce90f50b97139a63c',\n sizeBytes: 2_588_147_712, // 2.59GB (exact, HF LFS)\n // Conservative limit for 4GB RAM devices.\n // TODO: Benchmark during Phase 2 testing.\n contextWindow: 8_000,\n minRamBytes: 2_000_000_000, // 2GB — LiteRT-LM memory-maps weights, actual RSS ~1.5GB\n supportedPlatforms: ['ios', 'android'],\n },\n {\n id: 'gemma-e4b',\n name: 'Gemma 4 E4B',\n parameterCount: '4.5B',\n quantization: 'mixed-4/8-bit',\n downloadUrl:\n 'https://huggingface.co/litert-community/gemma-4-E4B-it-litert-lm/resolve/main/gemma-4-E4B-it.litertlm',\n sha256: '0b2a8980ce155fd97673d8e820b4d29d9c7d99b8fa6806f425d969b145bd52e0',\n sizeBytes: 3_659_530_240, // 3.66GB (exact, HF LFS)\n contextWindow: 16_000,\n minRamBytes: 3_000_000_000, // 3GB — LiteRT-LM memory-maps weights\n supportedPlatforms: ['ios', 'android'],\n },\n];\n\n/**\n * Look up a model registry entry by ID.\n * Returns undefined if not found.\n */\nexport function getRegistryEntry(modelId: string): ModelRegistryEntry | undefined {\n return MODEL_REGISTRY.find((m) => m.id === modelId);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model Registry\n *\n * Defines all downloadable models known to expo-ai-kit.\n * getDownloadableModels() reads from this registry and enriches\n * each entry with on-device status from the native layer.\n */\n\nexport type ModelRegistryEntry = {\n /** Unique model identifier used in setModel/downloadModel */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Parameter count label */\n parameterCount: string;\n /** Quantization variant */\n quantization: string;\n /** URL to download the LiteRT-LM model file */\n downloadUrl: string;\n /** SHA256 hash for integrity verification after download */\n sha256: string;\n /** Download file size in bytes */\n sizeBytes: number;\n /**\n * Practical context window (max tokens) for this model on constrained devices.\n *\n * These are conservative defaults, NOT the base model's theoretical max.\n * These values should be benchmarked and adjusted during testing with\n * real devices.\n */\n contextWindow: number;\n /** Minimum device RAM in bytes required to run this model */\n minRamBytes: number;\n /** Platforms this model can run on */\n supportedPlatforms: ('ios' | 'android')[];\n /**\n * License the model weights are distributed under — an SPDX identifier\n * (e.g. 'Apache-2.0', 'MIT') or a family name for non-OSI terms (e.g. 'Gemma',\n * 'Llama-3.2'). Surfaced on {@link DownloadableModel} so app developers can\n * check their obligations before shipping a model to users.\n */\n license: string;\n};\n\nexport const MODEL_REGISTRY: ModelRegistryEntry[] = [\n {\n id: 'gemma-e2b',\n name: 'Gemma 4 E2B',\n parameterCount: '2.3B',\n quantization: 'mixed-2/4/8-bit',\n downloadUrl:\n 'https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it.litertlm',\n sha256: '181938105e0eefd105961417e8da75903eacda102c4fce9ce90f50b97139a63c',\n sizeBytes: 2_588_147_712, // 2.59GB (exact, HF LFS)\n // Conservative limit for 4GB RAM devices.\n // TODO: Benchmark during Phase 2 testing.\n contextWindow: 8_000,\n minRamBytes: 2_000_000_000, // 2GB — LiteRT-LM memory-maps weights, actual RSS ~1.5GB\n supportedPlatforms: ['ios', 'android'],\n license: 'Gemma',\n },\n {\n id: 'gemma-e4b',\n name: 'Gemma 4 E4B',\n parameterCount: '4.5B',\n quantization: 'mixed-4/8-bit',\n downloadUrl:\n 'https://huggingface.co/litert-community/gemma-4-E4B-it-litert-lm/resolve/main/gemma-4-E4B-it.litertlm',\n sha256: '0b2a8980ce155fd97673d8e820b4d29d9c7d99b8fa6806f425d969b145bd52e0',\n sizeBytes: 3_659_530_240, // 3.66GB (exact, HF LFS)\n contextWindow: 16_000,\n minRamBytes: 3_000_000_000, // 3GB — LiteRT-LM memory-maps weights\n supportedPlatforms: ['ios', 'android'],\n license: 'Gemma',\n },\n // --- Qwen3 (Apache-2.0) — official litert-community builds. A size ladder\n // from a sub-GB model that runs anywhere up to a 4B that rivals Gemma E4B. ---\n {\n id: 'qwen3-0.6b',\n name: 'Qwen3 0.6B',\n parameterCount: '0.6B',\n quantization: 'mixed-int4',\n downloadUrl:\n 'https://huggingface.co/litert-community/Qwen3-0.6B/resolve/main/qwen3_0_6b_mixed_int4.litertlm',\n sha256: 'b1baab462f6be49d70eada79d715c2c52cd9ece0cad00bddf6a2c097d23498e9',\n sizeBytes: 497_664_000, // 475MB (exact, HF LFS)\n // The int4 build ships a 2048-token KV. TODO: benchmark on device.\n contextWindow: 2_048,\n minRamBytes: 1_000_000_000, // 1GB — tiny; runs on virtually any modern device\n supportedPlatforms: ['ios', 'android'],\n license: 'Apache-2.0',\n },\n {\n id: 'qwen3-1.7b',\n name: 'Qwen3 1.7B',\n parameterCount: '1.7B',\n quantization: 'dynamic-int8',\n downloadUrl:\n 'https://huggingface.co/litert-community/Qwen3-1.7B/resolve/main/Qwen3_1.7B.litertlm',\n sha256: '66064a4e9269cb693e124c4e3040bcb8a446b10bca42663896329495add3861c',\n sizeBytes: 2_056_729_520, // 2.06GB (exact, HF LFS)\n // Conservative default (not marked in the filename). TODO: benchmark on device.\n contextWindow: 4_096,\n minRamBytes: 2_000_000_000, // 2GB\n supportedPlatforms: ['ios', 'android'],\n license: 'Apache-2.0',\n },\n {\n id: 'qwen3-4b',\n name: 'Qwen3 4B',\n parameterCount: '4B',\n quantization: 'mixed-int4',\n downloadUrl:\n 'https://huggingface.co/litert-community/Qwen3-4B/resolve/main/qwen3_4b_mixed_int4.litertlm',\n sha256: 'f0794bc77efeaaf4f7af815f04c483b19b8f2ae4a102cef1b7b760a25848a18e',\n sizeBytes: 2_659_057_664, // 2.66GB (exact, HF LFS)\n // Conservative default (not marked in the filename). TODO: benchmark on device.\n contextWindow: 4_096,\n minRamBytes: 3_000_000_000, // 3GB — 4B params need more headroom than the similarly-sized E2B\n supportedPlatforms: ['ios', 'android'],\n license: 'Apache-2.0',\n },\n // --- Phi-4 Mini (MIT) — strong reasoning; q8 build, the heaviest downloadable. ---\n {\n id: 'phi-4-mini',\n name: 'Phi-4 Mini',\n parameterCount: '3.8B',\n quantization: 'int8',\n downloadUrl:\n 'https://huggingface.co/litert-community/Phi-4-mini-instruct/resolve/main/Phi-4-mini-instruct_multi-prefill-seq_q8_ekv4096.litertlm',\n sha256: '7764d4deb53800578307be33039476b38a6c370fff71bedb3c0552563e23ab02',\n sizeBytes: 3_910_090_752, // 3.91GB (exact, HF LFS)\n contextWindow: 4_096, // ekv4096 build\n minRamBytes: 4_000_000_000, // 4GB — q8 weights, heaviest downloadable\n supportedPlatforms: ['ios', 'android'],\n license: 'MIT',\n },\n];\n\n// ---------------------------------------------------------------------------\n// Custom (developer-registered) models — \"bring your own model\".\n//\n// The built-in MODEL_REGISTRY above is curated: each entry's SHA256 is pinned by\n// a maintainer who verified the bytes. registerModel() lets app developers add\n// any LiteRT-LM model under the same contract — they supply the metadata\n// (including the SHA256), so the integrity check still holds end-to-end. Custom\n// entries live in memory only; call registerModel() at startup on every launch\n// (the downloaded file on disk persists and is keyed by id, so status survives\n// restarts once you re-register).\n// ---------------------------------------------------------------------------\n\nconst customModels = new Map<string, ModelRegistryEntry>();\n\n/** Ids owned by the native built-in backends; not valid for custom models. */\nconst RESERVED_MODEL_IDS = new Set(['apple-fm', 'mlkit']);\n\nconst SHA256_RE = /^[a-f0-9]{64}$/i;\n\n/**\n * Validate a model entry, returning a list of human-readable problems\n * (empty ⇒ valid). Pure — used by {@link registerModel} and unit-tested.\n */\nexport function validateModelEntry(entry: ModelRegistryEntry): string[] {\n const errors: string[] = [];\n if (!entry || typeof entry !== 'object') return ['entry must be an object'];\n if (!entry.id || typeof entry.id !== 'string') errors.push('id is required');\n if (!entry.name || typeof entry.name !== 'string') errors.push('name is required');\n if (!entry.parameterCount) errors.push('parameterCount is required (e.g. \"1.7B\")');\n if (!entry.quantization) errors.push('quantization is required (e.g. \"int4\")');\n if (typeof entry.downloadUrl !== 'string' || !/^https?:\\/\\//.test(entry.downloadUrl)) {\n errors.push('downloadUrl must be an http(s) URL');\n }\n if (typeof entry.sha256 !== 'string' || !SHA256_RE.test(entry.sha256)) {\n errors.push('sha256 must be a 64-character hex string (use fetchModelMetadata to obtain it)');\n }\n if (!Number.isFinite(entry.sizeBytes) || entry.sizeBytes <= 0) {\n errors.push('sizeBytes must be a positive number');\n }\n if (!Number.isFinite(entry.minRamBytes) || entry.minRamBytes < 0) {\n errors.push('minRamBytes must be >= 0');\n }\n if (!Number.isInteger(entry.contextWindow) || entry.contextWindow <= 0) {\n errors.push('contextWindow must be a positive integer');\n }\n if (!Array.isArray(entry.supportedPlatforms) || entry.supportedPlatforms.length === 0) {\n errors.push('supportedPlatforms must list at least one of \"ios\" / \"android\"');\n } else if (!entry.supportedPlatforms.every((p) => p === 'ios' || p === 'android')) {\n errors.push('supportedPlatforms may only contain \"ios\" or \"android\"');\n }\n if (!entry.license || typeof entry.license !== 'string') {\n errors.push('license is required (e.g. \"Apache-2.0\", \"MIT\")');\n }\n return errors;\n}\n\n/**\n * Register a custom downloadable model at runtime.\n *\n * After registering, the id works with `downloadModel` / `setModel` /\n * `getDownloadableModels` exactly like a built-in. The download is integrity-\n * checked against the `sha256` you provide — pin a value you trust (see\n * {@link fetchModelMetadata}). Throws if the entry is invalid or the id\n * collides with a built-in (curated or native) model.\n *\n * @example\n * ```ts\n * const { sha256, sizeBytes } = await fetchModelMetadata(url); // dev-time\n * registerModel({\n * id: 'qwen3-8b', name: 'Qwen3 8B', parameterCount: '8B', quantization: 'int4',\n * downloadUrl: url, sha256, sizeBytes,\n * contextWindow: 4096, minRamBytes: 6_000_000_000,\n * supportedPlatforms: ['ios', 'android'], license: 'Apache-2.0',\n * });\n * ```\n */\nexport function registerModel(entry: ModelRegistryEntry): void {\n const errors = validateModelEntry(entry);\n if (errors.length > 0) {\n throw new Error(`registerModel: invalid model entry — ${errors.join('; ')}`);\n }\n if (RESERVED_MODEL_IDS.has(entry.id) || MODEL_REGISTRY.some((m) => m.id === entry.id)) {\n throw new Error(`registerModel: \"${entry.id}\" is a built-in model id; choose a different id`);\n }\n // Clone so later external mutation of the caller's object can't corrupt the registry.\n customModels.set(entry.id, { ...entry, supportedPlatforms: [...entry.supportedPlatforms] });\n}\n\n/**\n * Remove a previously {@link registerModel}'d custom model.\n * Returns true if one was removed. Does not delete any downloaded file —\n * use `deleteModel` for that. No-op for built-in models.\n */\nexport function unregisterModel(modelId: string): boolean {\n return customModels.delete(modelId);\n}\n\n/** All custom models registered via {@link registerModel}, in registration order. */\nexport function getRegisteredModels(): ModelRegistryEntry[] {\n return [...customModels.values()];\n}\n\n/** Built-in registry plus all custom models. */\nexport function getAllModels(): ModelRegistryEntry[] {\n return [...MODEL_REGISTRY, ...customModels.values()];\n}\n\n/**\n * Look up a model registry entry by ID (built-in or custom).\n * Returns undefined if not found.\n */\nexport function getRegistryEntry(modelId: string): ModelRegistryEntry | undefined {\n return MODEL_REGISTRY.find((m) => m.id === modelId) ?? customModels.get(modelId);\n}\n\n/**\n * Parse a HuggingFace \"resolve\" download URL into its parts.\n * Returns null if the URL isn't a HuggingFace resolve URL. Pure — unit-tested.\n *\n * e.g. https://huggingface.co/litert-community/Qwen3-0.6B/resolve/main/model.litertlm\n * → { repo: 'litert-community/Qwen3-0.6B', revision: 'main', path: 'model.litertlm' }\n */\nexport function parseHuggingFaceUrl(\n url: string\n): { repo: string; revision: string; path: string } | null {\n if (typeof url !== 'string') return null;\n const clean = url.split('#')[0].split('?')[0];\n const m = /^https?:\\/\\/huggingface\\.co\\/([^/]+\\/[^/]+)\\/resolve\\/([^/]+)\\/(.+)$/.exec(clean);\n if (!m) return null;\n return { repo: m[1], revision: m[2], path: decodeURIComponent(m[3]) };\n}\n\n/**\n * Look up a model file's SHA256 and byte size from HuggingFace, so you can fill\n * in a {@link registerModel} entry without computing them by hand.\n *\n * Trust note: this reads the hash from the same host you'll download from, so it\n * only guards against download corruption — NOT a maliciously changed upstream\n * repo. For a real supply-chain guarantee, run this once at dev time and PIN the\n * returned `sha256` in your source, exactly like the built-in registry.\n *\n * @param downloadUrl - A HuggingFace resolve URL (the one you'll register).\n * @returns `{ sha256, sizeBytes }` ready to spread into a registry entry.\n * @throws if the URL isn't a HuggingFace resolve URL, the API call fails, or the\n * file isn't an LFS object (no hash/size available).\n */\nexport async function fetchModelMetadata(\n downloadUrl: string\n): Promise<{ sha256: string; sizeBytes: number }> {\n const parsed = parseHuggingFaceUrl(downloadUrl);\n if (!parsed) {\n throw new Error(\n 'fetchModelMetadata: expected a HuggingFace resolve URL ' +\n '(https://huggingface.co/<owner>/<repo>/resolve/<revision>/<file>)'\n );\n }\n const { repo, revision, path } = parsed;\n const slash = path.lastIndexOf('/');\n const dir = slash >= 0 ? path.slice(0, slash) : '';\n const treeUrl =\n `https://huggingface.co/api/models/${repo}/tree/${revision}` + (dir ? `/${dir}` : '');\n\n const res = await fetch(treeUrl);\n if (!res.ok) {\n throw new Error(`fetchModelMetadata: HuggingFace API returned ${res.status} for ${treeUrl}`);\n }\n const items = (await res.json()) as Array<{\n path?: string;\n size?: number;\n lfs?: { oid?: string; size?: number };\n }>;\n const file = Array.isArray(items) ? items.find((it) => it?.path === path) : undefined;\n if (!file) {\n throw new Error(`fetchModelMetadata: \"${path}\" not found in ${repo}@${revision}`);\n }\n const sha256 = file.lfs?.oid;\n const sizeBytes = file.lfs?.size ?? file.size;\n if (!sha256 || !sizeBytes) {\n throw new Error(\n `fetchModelMetadata: \"${path}\" has no LFS hash/size — is it the actual model weight file?`\n );\n }\n return { sha256, sizeBytes };\n}\n"]}
|
package/build/types.d.ts
CHANGED
|
@@ -329,6 +329,8 @@ export type DownloadableModel = {
|
|
|
329
329
|
name: string;
|
|
330
330
|
/** Parameter count label (e.g. '2.3B') */
|
|
331
331
|
parameterCount: string;
|
|
332
|
+
/** License the weights are distributed under (e.g. 'Apache-2.0', 'MIT', 'Gemma'). */
|
|
333
|
+
license: string;
|
|
332
334
|
/** Download file size in bytes */
|
|
333
335
|
sizeBytes: number;
|
|
334
336
|
/** Maximum context window in tokens */
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/build/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAyaA;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,IAAI,CAAiB;IACrB,OAAO,CAAS;IAEhB,YAAY,IAAoB,EAAE,OAAe,EAAE,OAAgB;QACjE,KAAK,CAAC,OAAO,IAAI,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF","sourcesContent":["/**\n * Hardware backend for on-device model inference.\n *\n * - 'auto': Try GPU first, fall back to CPU (default)\n * - 'gpu': Force GPU — faster (~40-50 tok/s) but needs more memory\n * - 'cpu': Force CPU — slower (~2-5 tok/s) but works on low-RAM devices\n */\nexport type InferenceBackend = 'auto' | 'gpu' | 'cpu';\n\n/**\n * Sampling / generation parameters applied to a model session.\n *\n * Support is per-backend (on-device runtimes expose different knobs), so these\n * are best-effort — unsupported fields are ignored rather than erroring:\n *\n * | field | Gemma (LiteRT-LM) | Apple Foundation Models | ML Kit |\n * |-------------|:-----------------:|:-----------------------:|:------:|\n * | temperature | ✓ | ✓ | — |\n * | topK | ✓ | — | — |\n * | topP | ✓ | — | — |\n * | seed | ✓ (iOS only) | — | — |\n * | maxTokens | — | ✓ (max output) | — |\n *\n * Notes:\n * - Gemma/LiteRT-LM has no per-generation output-token cap (its `maxNumTokens`\n * is the total KV-cache size, set at load), so `maxTokens` is not honored\n * there. Its sampler (topK/topP/temperature[/seed]) is fixed at conversation\n * creation, which is why generation config lives on setModel() rather than\n * per-call. `seed` is currently wired on iOS only.\n * - The ML Kit built-in (Android default) does not yet apply generation config;\n * it uses its own defaults.\n */\nexport type GenerationConfig = {\n /** Sampling temperature. Lower = more deterministic. Typically 0.0–2.0. */\n temperature?: number;\n /** Nucleus sampling: number of top logits to consider. Must be > 0. */\n topK?: number;\n /** Nucleus sampling: cumulative probability threshold in [0, 1]. */\n topP?: number;\n /** RNG seed for reproducible sampling (Gemma only). */\n seed?: number;\n /** Maximum number of output tokens to generate (Apple FM / ML Kit only). */\n maxTokens?: number;\n};\n\n/**\n * Options for setModel.\n */\nexport type SetModelOptions = {\n /** Hardware backend to use for inference. Defaults to 'auto'. */\n backend?: InferenceBackend;\n /**\n * Default sampling parameters for this model session. Applied when the model\n * is activated and used for all subsequent sendMessage/streamMessage calls\n * until the next setModel(). See {@link GenerationConfig} for per-backend support.\n */\n generation?: GenerationConfig;\n};\n\n/**\n * Role in a conversation message.\n */\nexport type LLMRole = 'system' | 'user' | 'assistant';\n\n/**\n * A single message in a conversation.\n */\nexport type LLMMessage = {\n role: LLMRole;\n content: string;\n};\n\n/**\n * Options for sendMessage.\n */\nexport type LLMSendOptions = {\n /**\n * Default system prompt to use if no system message is provided in the messages array.\n * If a system message exists in the array, this is ignored.\n */\n systemPrompt?: string;\n /**\n * Abort the request. When the signal fires, the returned promise rejects with\n * an INFERENCE_CANCELLED {@link ModelError}.\n *\n * Note: on-device, non-streaming generation cannot always be interrupted\n * mid-decode — abort always unblocks the caller, but the model may keep\n * computing in the background until it finishes, during which a new\n * sendMessage/streamMessage will throw INFERENCE_BUSY. To truly interrupt a\n * long generation, prefer streamMessage().stop().\n */\n signal?: AbortSignal;\n};\n\n/**\n * Response from sendMessage.\n */\nexport type LLMResponse = {\n /** The generated response text */\n text: string;\n};\n\n/**\n * Options for streamMessage.\n */\nexport type LLMStreamOptions = {\n /**\n * Default system prompt to use if no system message is provided in the messages array.\n * If a system message exists in the array, this is ignored.\n */\n systemPrompt?: string;\n};\n\n/**\n * Handle returned by streamMessage.\n */\nexport type LLMStreamHandle = {\n /** Resolves with the final text when streaming completes or is stopped. */\n promise: Promise<LLMResponse>;\n /** Stop streaming. Resolves `promise` with the text accumulated so far. */\n stop: () => void;\n};\n\n/**\n * Event payload for streaming tokens.\n */\nexport type LLMStreamEvent = {\n /** Unique identifier for this streaming session */\n sessionId: string;\n /** The token/chunk of text received */\n token: string;\n /** Accumulated text so far */\n accumulatedText: string;\n /** Whether this is the final chunk */\n isDone: boolean;\n};\n\n/**\n * Callback function for streaming events.\n */\nexport type LLMStreamCallback = (event: LLMStreamEvent) => void;\n\n\n// ============================================================================\n// Structured Output (generateObject)\n// ============================================================================\n\n/**\n * The set of JSON Schema primitive `type` values understood by\n * {@link generateObject}'s local validator.\n */\nexport type JSONSchemaType =\n | 'object'\n | 'array'\n | 'string'\n | 'number'\n | 'integer'\n | 'boolean'\n | 'null';\n\n/**\n * A JSON Schema describing the shape you want {@link generateObject} to return.\n *\n * A pragmatic subset is enforced locally — `type`, `properties`, `required`,\n * `items`, and `enum` — which covers most extraction shapes. Any other JSON\n * Schema keywords you include (e.g. `description`, `minLength`) are still sent\n * to the model to guide it, but are not validated on-device. Keep schemas small:\n * on-device models follow flat, shallow shapes far more reliably than deeply\n * nested ones.\n */\nexport type JSONSchema = {\n /** Expected JSON type (or a union of types). */\n type?: JSONSchemaType | JSONSchemaType[];\n /** Human-readable hint passed through to the model. */\n description?: string;\n /** For `object` schemas: the schema of each named property. */\n properties?: Record<string, JSONSchema>;\n /** For `object` schemas: property names that must be present. */\n required?: string[];\n /** For `array` schemas: the schema each element must satisfy. */\n items?: JSONSchema;\n /** Restrict the value to this set of literals. */\n enum?: ReadonlyArray<string | number | boolean | null>;\n /** Other JSON Schema keywords are accepted and forwarded to the model. */\n [key: string]: unknown;\n};\n\n/**\n * Options for {@link generateObject}.\n */\nexport type GenerateObjectOptions = {\n /**\n * System prompt used when the messages array has no system message. Defaults\n * to a structured-output-oriented instruction. If a system message is present\n * in the array, this is ignored (the schema instruction is appended to it).\n */\n systemPrompt?: string;\n /**\n * Abort the request. Behaves like {@link LLMSendOptions.signal} — the returned\n * promise rejects with an INFERENCE_CANCELLED {@link ModelError}.\n */\n signal?: AbortSignal;\n /**\n * How many times to re-prompt the model when its output is not valid JSON or\n * does not match the schema. Each repair feeds the error back to the model.\n * Defaults to 2 (i.e. up to 3 generations total).\n */\n maxRepairAttempts?: number;\n};\n\n/**\n * Result of {@link generateObject}.\n */\nexport type GenerateObjectResult<T = unknown> = {\n /** The parsed value, validated against the schema. */\n object: T;\n /** The raw model output that produced `object` (useful for debugging). */\n text: string;\n};\n\n\n// ============================================================================\n// Tool / Function Calling (generateText)\n// ============================================================================\n\n/**\n * A tool (function) the model may call to fetch data or take an action.\n *\n * The model never runs anything itself — it *proposes* a call (a name + JSON\n * arguments), {@link generateText} validates the arguments against `parameters`,\n * and only then invokes your `execute`. The result is fed back into the\n * conversation so the model can use it to produce its final answer.\n *\n * @typeParam TArgs - Shape of the validated arguments passed to `execute`.\n * @typeParam TResult - What `execute` returns (serialized back to the model).\n */\nexport type Tool<TArgs = any, TResult = any> = {\n /**\n * What the tool does and when to use it. This is how the model decides\n * whether a request matches this tool — make it specific and action-oriented.\n */\n description: string;\n /**\n * JSON Schema for the tool's arguments. The model is told to conform to it,\n * and the args it proposes are validated against it (same pragmatic subset as\n * {@link generateObject}) before `execute` runs. Keep it small and shallow.\n */\n parameters: JSONSchema;\n /**\n * Runs the tool with the validated arguments and returns a result.\n *\n * **Optional on purpose.** If you omit it, {@link generateText} does not run\n * anything — it stops with `finishReason: 'tool-calls'` and hands you the\n * proposed call so you can confirm, gate, or execute it yourself.\n */\n execute?: (args: TArgs) => TResult | Promise<TResult>;\n};\n\n/** A map of tool name → {@link Tool}, passed to {@link generateText}. */\nexport type ToolSet = Record<string, Tool>;\n\n/** A tool invocation the model proposed (name + validated arguments). */\nexport type ToolCall = {\n /** The tool's key in the {@link ToolSet}. */\n toolName: string;\n /** Arguments the model supplied, validated against the tool's `parameters`. */\n args: unknown;\n};\n\n/** The outcome of running a {@link ToolCall} via its `execute`. */\nexport type ToolResult = {\n /** The tool's key in the {@link ToolSet}. */\n toolName: string;\n /** The arguments that were passed to `execute`. */\n args: unknown;\n /** Whatever `execute` returned (or `{ error }` if it threw). */\n result: unknown;\n};\n\n/** One model round-trip in the {@link generateText} loop. */\nexport type StepResult = {\n /** Assistant text produced this step (empty when the step only called a tool). */\n text: string;\n /** Tool calls proposed this step (at most one in the current protocol). */\n toolCalls: ToolCall[];\n /** Results of the tool calls executed this step. */\n toolResults: ToolResult[];\n};\n\n/**\n * Why {@link generateText} stopped.\n *\n * - `'stop'`: the model produced a final text answer.\n * - `'tool-calls'`: stopped because a proposed tool has no `execute` — the call\n * is returned for you to handle (human-in-the-loop).\n * - `'max-steps'`: hit the `maxSteps` cap while still calling tools. Raise the cap.\n */\nexport type GenerateTextFinishReason = 'stop' | 'tool-calls' | 'max-steps';\n\n/**\n * Options for {@link generateText}.\n */\nexport type GenerateTextOptions = {\n /** Tools the model may call. Omit (or pass `{}`) for a plain text generation. */\n tools?: ToolSet;\n /**\n * Maximum number of model round-trips (each call + tool execution is one step).\n * Bounds the tool-calling chain so it can't run away. Defaults to 5.\n */\n maxSteps?: number;\n /**\n * System prompt used when the messages array has no system message. The tool\n * instructions are appended to it (or to the array's system message if present).\n */\n systemPrompt?: string;\n /**\n * Abort the request. Behaves like {@link LLMSendOptions.signal} — the returned\n * promise rejects with an INFERENCE_CANCELLED {@link ModelError}.\n */\n signal?: AbortSignal;\n /**\n * How many times to re-prompt within a step when the model emits a malformed\n * tool call, an unknown tool name, or arguments that fail schema validation.\n * Defaults to 2. If it still can't comply, `generateText` throws INFERENCE_FAILED.\n */\n maxRepairAttempts?: number;\n};\n\n/**\n * Result of {@link generateText}.\n */\nexport type GenerateTextResult = {\n /** The final assistant text (empty if it stopped on a tool call without `execute`). */\n text: string;\n /** Every step taken, in order — useful for tracing or debugging. */\n steps: StepResult[];\n /** All tool calls across every step, flattened. */\n toolCalls: ToolCall[];\n /** All tool results across every step, flattened. */\n toolResults: ToolResult[];\n /** Why generation stopped. See {@link GenerateTextFinishReason}. */\n finishReason: GenerateTextFinishReason;\n};\n\n\n// ============================================================================\n// Model Types\n// ============================================================================\n\n/**\n * A built-in model provided by the OS (e.g. Apple Foundation Models, ML Kit).\n * These are always available on supported devices -- no download needed.\n */\nexport type BuiltInModel = {\n /** Unique model identifier (e.g. 'apple-fm', 'mlkit') */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Whether this model is available on the current device/OS */\n available: boolean;\n /** Platform this model is associated with */\n platform: 'ios' | 'android';\n /** Maximum context window in tokens */\n contextWindow: number;\n};\n\n/**\n * A downloadable model that the user manages (download, load, delete).\n * These require explicit download before use.\n */\nexport type DownloadableModel = {\n /** Unique model identifier (e.g. 'gemma-e2b', 'gemma-e4b') */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Parameter count label (e.g. '2.3B') */\n parameterCount: string;\n /** Download file size in bytes */\n sizeBytes: number;\n /** Maximum context window in tokens */\n contextWindow: number;\n /** Minimum device RAM in bytes required to run */\n minRamBytes: number;\n /** Whether this device meets the model's minimum RAM requirement */\n meetsRequirements: boolean;\n /** Current lifecycle status */\n status: DownloadableModelStatus;\n};\n\n/**\n * Lifecycle status of a downloadable model.\n *\n * - 'not-downloaded': Model file is not on disk\n * - 'downloading': Model file is being downloaded\n * - 'downloaded': Model file is on disk but not loaded into memory. Call\n * setModel() to load it. This survives app restarts, so use it to decide\n * whether a (re-)download is needed.\n * - 'loading': File is on disk, model is being loaded into memory for inference\n * - 'ready': Model is loaded in memory and ready for inference\n */\nexport type DownloadableModelStatus =\n | 'not-downloaded'\n | 'downloading'\n | 'downloaded'\n | 'loading'\n | 'ready';\n\n/**\n * Error codes for model-related operations.\n */\nexport type ModelErrorCode =\n | 'MODEL_NOT_FOUND'\n | 'MODEL_NOT_DOWNLOADED'\n | 'DOWNLOAD_FAILED'\n | 'DOWNLOAD_CORRUPT'\n | 'DOWNLOAD_STORAGE_FULL'\n | 'DOWNLOAD_CANCELLED'\n | 'INFERENCE_OOM'\n | 'INFERENCE_FAILED'\n | 'INFERENCE_BUSY'\n | 'INFERENCE_CANCELLED'\n | 'MODEL_LOAD_FAILED'\n | 'DEVICE_NOT_SUPPORTED'\n | 'UNKNOWN';\n\n/**\n * Structured error for model operations.\n */\nexport class ModelError extends Error {\n code: ModelErrorCode;\n modelId: string;\n\n constructor(code: ModelErrorCode, modelId: string, message?: string) {\n super(message ?? `${code}: ${modelId}`);\n this.name = 'ModelError';\n this.code = code;\n this.modelId = modelId;\n }\n}\n\n/**\n * Event payload for model download progress.\n */\nexport type ModelDownloadProgressEvent = {\n /** Model being downloaded */\n modelId: string;\n /** Download progress from 0 to 1 */\n progress: number;\n};\n\n/**\n * Event payload for model state changes.\n */\nexport type ModelStateChangeEvent = {\n /** Model whose state changed */\n modelId: string;\n /** New status */\n status: DownloadableModelStatus;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA2aA;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,IAAI,CAAiB;IACrB,OAAO,CAAS;IAEhB,YAAY,IAAoB,EAAE,OAAe,EAAE,OAAgB;QACjE,KAAK,CAAC,OAAO,IAAI,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF","sourcesContent":["/**\n * Hardware backend for on-device model inference.\n *\n * - 'auto': Try GPU first, fall back to CPU (default)\n * - 'gpu': Force GPU — faster (~40-50 tok/s) but needs more memory\n * - 'cpu': Force CPU — slower (~2-5 tok/s) but works on low-RAM devices\n */\nexport type InferenceBackend = 'auto' | 'gpu' | 'cpu';\n\n/**\n * Sampling / generation parameters applied to a model session.\n *\n * Support is per-backend (on-device runtimes expose different knobs), so these\n * are best-effort — unsupported fields are ignored rather than erroring:\n *\n * | field | Gemma (LiteRT-LM) | Apple Foundation Models | ML Kit |\n * |-------------|:-----------------:|:-----------------------:|:------:|\n * | temperature | ✓ | ✓ | — |\n * | topK | ✓ | — | — |\n * | topP | ✓ | — | — |\n * | seed | ✓ (iOS only) | — | — |\n * | maxTokens | — | ✓ (max output) | — |\n *\n * Notes:\n * - Gemma/LiteRT-LM has no per-generation output-token cap (its `maxNumTokens`\n * is the total KV-cache size, set at load), so `maxTokens` is not honored\n * there. Its sampler (topK/topP/temperature[/seed]) is fixed at conversation\n * creation, which is why generation config lives on setModel() rather than\n * per-call. `seed` is currently wired on iOS only.\n * - The ML Kit built-in (Android default) does not yet apply generation config;\n * it uses its own defaults.\n */\nexport type GenerationConfig = {\n /** Sampling temperature. Lower = more deterministic. Typically 0.0–2.0. */\n temperature?: number;\n /** Nucleus sampling: number of top logits to consider. Must be > 0. */\n topK?: number;\n /** Nucleus sampling: cumulative probability threshold in [0, 1]. */\n topP?: number;\n /** RNG seed for reproducible sampling (Gemma only). */\n seed?: number;\n /** Maximum number of output tokens to generate (Apple FM / ML Kit only). */\n maxTokens?: number;\n};\n\n/**\n * Options for setModel.\n */\nexport type SetModelOptions = {\n /** Hardware backend to use for inference. Defaults to 'auto'. */\n backend?: InferenceBackend;\n /**\n * Default sampling parameters for this model session. Applied when the model\n * is activated and used for all subsequent sendMessage/streamMessage calls\n * until the next setModel(). See {@link GenerationConfig} for per-backend support.\n */\n generation?: GenerationConfig;\n};\n\n/**\n * Role in a conversation message.\n */\nexport type LLMRole = 'system' | 'user' | 'assistant';\n\n/**\n * A single message in a conversation.\n */\nexport type LLMMessage = {\n role: LLMRole;\n content: string;\n};\n\n/**\n * Options for sendMessage.\n */\nexport type LLMSendOptions = {\n /**\n * Default system prompt to use if no system message is provided in the messages array.\n * If a system message exists in the array, this is ignored.\n */\n systemPrompt?: string;\n /**\n * Abort the request. When the signal fires, the returned promise rejects with\n * an INFERENCE_CANCELLED {@link ModelError}.\n *\n * Note: on-device, non-streaming generation cannot always be interrupted\n * mid-decode — abort always unblocks the caller, but the model may keep\n * computing in the background until it finishes, during which a new\n * sendMessage/streamMessage will throw INFERENCE_BUSY. To truly interrupt a\n * long generation, prefer streamMessage().stop().\n */\n signal?: AbortSignal;\n};\n\n/**\n * Response from sendMessage.\n */\nexport type LLMResponse = {\n /** The generated response text */\n text: string;\n};\n\n/**\n * Options for streamMessage.\n */\nexport type LLMStreamOptions = {\n /**\n * Default system prompt to use if no system message is provided in the messages array.\n * If a system message exists in the array, this is ignored.\n */\n systemPrompt?: string;\n};\n\n/**\n * Handle returned by streamMessage.\n */\nexport type LLMStreamHandle = {\n /** Resolves with the final text when streaming completes or is stopped. */\n promise: Promise<LLMResponse>;\n /** Stop streaming. Resolves `promise` with the text accumulated so far. */\n stop: () => void;\n};\n\n/**\n * Event payload for streaming tokens.\n */\nexport type LLMStreamEvent = {\n /** Unique identifier for this streaming session */\n sessionId: string;\n /** The token/chunk of text received */\n token: string;\n /** Accumulated text so far */\n accumulatedText: string;\n /** Whether this is the final chunk */\n isDone: boolean;\n};\n\n/**\n * Callback function for streaming events.\n */\nexport type LLMStreamCallback = (event: LLMStreamEvent) => void;\n\n\n// ============================================================================\n// Structured Output (generateObject)\n// ============================================================================\n\n/**\n * The set of JSON Schema primitive `type` values understood by\n * {@link generateObject}'s local validator.\n */\nexport type JSONSchemaType =\n | 'object'\n | 'array'\n | 'string'\n | 'number'\n | 'integer'\n | 'boolean'\n | 'null';\n\n/**\n * A JSON Schema describing the shape you want {@link generateObject} to return.\n *\n * A pragmatic subset is enforced locally — `type`, `properties`, `required`,\n * `items`, and `enum` — which covers most extraction shapes. Any other JSON\n * Schema keywords you include (e.g. `description`, `minLength`) are still sent\n * to the model to guide it, but are not validated on-device. Keep schemas small:\n * on-device models follow flat, shallow shapes far more reliably than deeply\n * nested ones.\n */\nexport type JSONSchema = {\n /** Expected JSON type (or a union of types). */\n type?: JSONSchemaType | JSONSchemaType[];\n /** Human-readable hint passed through to the model. */\n description?: string;\n /** For `object` schemas: the schema of each named property. */\n properties?: Record<string, JSONSchema>;\n /** For `object` schemas: property names that must be present. */\n required?: string[];\n /** For `array` schemas: the schema each element must satisfy. */\n items?: JSONSchema;\n /** Restrict the value to this set of literals. */\n enum?: ReadonlyArray<string | number | boolean | null>;\n /** Other JSON Schema keywords are accepted and forwarded to the model. */\n [key: string]: unknown;\n};\n\n/**\n * Options for {@link generateObject}.\n */\nexport type GenerateObjectOptions = {\n /**\n * System prompt used when the messages array has no system message. Defaults\n * to a structured-output-oriented instruction. If a system message is present\n * in the array, this is ignored (the schema instruction is appended to it).\n */\n systemPrompt?: string;\n /**\n * Abort the request. Behaves like {@link LLMSendOptions.signal} — the returned\n * promise rejects with an INFERENCE_CANCELLED {@link ModelError}.\n */\n signal?: AbortSignal;\n /**\n * How many times to re-prompt the model when its output is not valid JSON or\n * does not match the schema. Each repair feeds the error back to the model.\n * Defaults to 2 (i.e. up to 3 generations total).\n */\n maxRepairAttempts?: number;\n};\n\n/**\n * Result of {@link generateObject}.\n */\nexport type GenerateObjectResult<T = unknown> = {\n /** The parsed value, validated against the schema. */\n object: T;\n /** The raw model output that produced `object` (useful for debugging). */\n text: string;\n};\n\n\n// ============================================================================\n// Tool / Function Calling (generateText)\n// ============================================================================\n\n/**\n * A tool (function) the model may call to fetch data or take an action.\n *\n * The model never runs anything itself — it *proposes* a call (a name + JSON\n * arguments), {@link generateText} validates the arguments against `parameters`,\n * and only then invokes your `execute`. The result is fed back into the\n * conversation so the model can use it to produce its final answer.\n *\n * @typeParam TArgs - Shape of the validated arguments passed to `execute`.\n * @typeParam TResult - What `execute` returns (serialized back to the model).\n */\nexport type Tool<TArgs = any, TResult = any> = {\n /**\n * What the tool does and when to use it. This is how the model decides\n * whether a request matches this tool — make it specific and action-oriented.\n */\n description: string;\n /**\n * JSON Schema for the tool's arguments. The model is told to conform to it,\n * and the args it proposes are validated against it (same pragmatic subset as\n * {@link generateObject}) before `execute` runs. Keep it small and shallow.\n */\n parameters: JSONSchema;\n /**\n * Runs the tool with the validated arguments and returns a result.\n *\n * **Optional on purpose.** If you omit it, {@link generateText} does not run\n * anything — it stops with `finishReason: 'tool-calls'` and hands you the\n * proposed call so you can confirm, gate, or execute it yourself.\n */\n execute?: (args: TArgs) => TResult | Promise<TResult>;\n};\n\n/** A map of tool name → {@link Tool}, passed to {@link generateText}. */\nexport type ToolSet = Record<string, Tool>;\n\n/** A tool invocation the model proposed (name + validated arguments). */\nexport type ToolCall = {\n /** The tool's key in the {@link ToolSet}. */\n toolName: string;\n /** Arguments the model supplied, validated against the tool's `parameters`. */\n args: unknown;\n};\n\n/** The outcome of running a {@link ToolCall} via its `execute`. */\nexport type ToolResult = {\n /** The tool's key in the {@link ToolSet}. */\n toolName: string;\n /** The arguments that were passed to `execute`. */\n args: unknown;\n /** Whatever `execute` returned (or `{ error }` if it threw). */\n result: unknown;\n};\n\n/** One model round-trip in the {@link generateText} loop. */\nexport type StepResult = {\n /** Assistant text produced this step (empty when the step only called a tool). */\n text: string;\n /** Tool calls proposed this step (at most one in the current protocol). */\n toolCalls: ToolCall[];\n /** Results of the tool calls executed this step. */\n toolResults: ToolResult[];\n};\n\n/**\n * Why {@link generateText} stopped.\n *\n * - `'stop'`: the model produced a final text answer.\n * - `'tool-calls'`: stopped because a proposed tool has no `execute` — the call\n * is returned for you to handle (human-in-the-loop).\n * - `'max-steps'`: hit the `maxSteps` cap while still calling tools. Raise the cap.\n */\nexport type GenerateTextFinishReason = 'stop' | 'tool-calls' | 'max-steps';\n\n/**\n * Options for {@link generateText}.\n */\nexport type GenerateTextOptions = {\n /** Tools the model may call. Omit (or pass `{}`) for a plain text generation. */\n tools?: ToolSet;\n /**\n * Maximum number of model round-trips (each call + tool execution is one step).\n * Bounds the tool-calling chain so it can't run away. Defaults to 5.\n */\n maxSteps?: number;\n /**\n * System prompt used when the messages array has no system message. The tool\n * instructions are appended to it (or to the array's system message if present).\n */\n systemPrompt?: string;\n /**\n * Abort the request. Behaves like {@link LLMSendOptions.signal} — the returned\n * promise rejects with an INFERENCE_CANCELLED {@link ModelError}.\n */\n signal?: AbortSignal;\n /**\n * How many times to re-prompt within a step when the model emits a malformed\n * tool call, an unknown tool name, or arguments that fail schema validation.\n * Defaults to 2. If it still can't comply, `generateText` throws INFERENCE_FAILED.\n */\n maxRepairAttempts?: number;\n};\n\n/**\n * Result of {@link generateText}.\n */\nexport type GenerateTextResult = {\n /** The final assistant text (empty if it stopped on a tool call without `execute`). */\n text: string;\n /** Every step taken, in order — useful for tracing or debugging. */\n steps: StepResult[];\n /** All tool calls across every step, flattened. */\n toolCalls: ToolCall[];\n /** All tool results across every step, flattened. */\n toolResults: ToolResult[];\n /** Why generation stopped. See {@link GenerateTextFinishReason}. */\n finishReason: GenerateTextFinishReason;\n};\n\n\n// ============================================================================\n// Model Types\n// ============================================================================\n\n/**\n * A built-in model provided by the OS (e.g. Apple Foundation Models, ML Kit).\n * These are always available on supported devices -- no download needed.\n */\nexport type BuiltInModel = {\n /** Unique model identifier (e.g. 'apple-fm', 'mlkit') */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Whether this model is available on the current device/OS */\n available: boolean;\n /** Platform this model is associated with */\n platform: 'ios' | 'android';\n /** Maximum context window in tokens */\n contextWindow: number;\n};\n\n/**\n * A downloadable model that the user manages (download, load, delete).\n * These require explicit download before use.\n */\nexport type DownloadableModel = {\n /** Unique model identifier (e.g. 'gemma-e2b', 'gemma-e4b') */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Parameter count label (e.g. '2.3B') */\n parameterCount: string;\n /** License the weights are distributed under (e.g. 'Apache-2.0', 'MIT', 'Gemma'). */\n license: string;\n /** Download file size in bytes */\n sizeBytes: number;\n /** Maximum context window in tokens */\n contextWindow: number;\n /** Minimum device RAM in bytes required to run */\n minRamBytes: number;\n /** Whether this device meets the model's minimum RAM requirement */\n meetsRequirements: boolean;\n /** Current lifecycle status */\n status: DownloadableModelStatus;\n};\n\n/**\n * Lifecycle status of a downloadable model.\n *\n * - 'not-downloaded': Model file is not on disk\n * - 'downloading': Model file is being downloaded\n * - 'downloaded': Model file is on disk but not loaded into memory. Call\n * setModel() to load it. This survives app restarts, so use it to decide\n * whether a (re-)download is needed.\n * - 'loading': File is on disk, model is being loaded into memory for inference\n * - 'ready': Model is loaded in memory and ready for inference\n */\nexport type DownloadableModelStatus =\n | 'not-downloaded'\n | 'downloading'\n | 'downloaded'\n | 'loading'\n | 'ready';\n\n/**\n * Error codes for model-related operations.\n */\nexport type ModelErrorCode =\n | 'MODEL_NOT_FOUND'\n | 'MODEL_NOT_DOWNLOADED'\n | 'DOWNLOAD_FAILED'\n | 'DOWNLOAD_CORRUPT'\n | 'DOWNLOAD_STORAGE_FULL'\n | 'DOWNLOAD_CANCELLED'\n | 'INFERENCE_OOM'\n | 'INFERENCE_FAILED'\n | 'INFERENCE_BUSY'\n | 'INFERENCE_CANCELLED'\n | 'MODEL_LOAD_FAILED'\n | 'DEVICE_NOT_SUPPORTED'\n | 'UNKNOWN';\n\n/**\n * Structured error for model operations.\n */\nexport class ModelError extends Error {\n code: ModelErrorCode;\n modelId: string;\n\n constructor(code: ModelErrorCode, modelId: string, message?: string) {\n super(message ?? `${code}: ${modelId}`);\n this.name = 'ModelError';\n this.code = code;\n this.modelId = modelId;\n }\n}\n\n/**\n * Event payload for model download progress.\n */\nexport type ModelDownloadProgressEvent = {\n /** Model being downloaded */\n modelId: string;\n /** Download progress from 0 to 1 */\n progress: number;\n};\n\n/**\n * Event payload for model state changes.\n */\nexport type ModelStateChangeEvent = {\n /** Model whose state changed */\n modelId: string;\n /** New status */\n status: DownloadableModelStatus;\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-ai-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "On-device AI for Expo — run Gemma 4, Apple Foundation Models & ML Kit locally: streaming, structured output, tool calling, zero API keys",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -38,6 +38,9 @@
|
|
|
38
38
|
"expo",
|
|
39
39
|
"gemma",
|
|
40
40
|
"gemma-4",
|
|
41
|
+
"qwen",
|
|
42
|
+
"qwen3",
|
|
43
|
+
"phi-4",
|
|
41
44
|
"on-device-ai",
|
|
42
45
|
"llm",
|
|
43
46
|
"litert",
|
package/src/index.ts
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
buildToolArgsRepair,
|
|
38
38
|
formatToolResult,
|
|
39
39
|
} from './tools';
|
|
40
|
-
import {
|
|
40
|
+
import { getAllModels, getRegistryEntry } from './models';
|
|
41
41
|
|
|
42
42
|
export * from './types';
|
|
43
43
|
export * from './models';
|
|
@@ -761,7 +761,7 @@ export async function getDownloadableModels(): Promise<DownloadableModel[]> {
|
|
|
761
761
|
return [];
|
|
762
762
|
}
|
|
763
763
|
|
|
764
|
-
const platformModels =
|
|
764
|
+
const platformModels = getAllModels().filter((entry) =>
|
|
765
765
|
entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')
|
|
766
766
|
);
|
|
767
767
|
|
|
@@ -781,6 +781,7 @@ export async function getDownloadableModels(): Promise<DownloadableModel[]> {
|
|
|
781
781
|
id: entry.id,
|
|
782
782
|
name: entry.name,
|
|
783
783
|
parameterCount: entry.parameterCount,
|
|
784
|
+
license: entry.license,
|
|
784
785
|
sizeBytes: entry.sizeBytes,
|
|
785
786
|
contextWindow: entry.contextWindow,
|
|
786
787
|
minRamBytes: entry.minRamBytes,
|
package/src/models.ts
CHANGED
|
@@ -33,6 +33,13 @@ export type ModelRegistryEntry = {
|
|
|
33
33
|
minRamBytes: number;
|
|
34
34
|
/** Platforms this model can run on */
|
|
35
35
|
supportedPlatforms: ('ios' | 'android')[];
|
|
36
|
+
/**
|
|
37
|
+
* License the model weights are distributed under — an SPDX identifier
|
|
38
|
+
* (e.g. 'Apache-2.0', 'MIT') or a family name for non-OSI terms (e.g. 'Gemma',
|
|
39
|
+
* 'Llama-3.2'). Surfaced on {@link DownloadableModel} so app developers can
|
|
40
|
+
* check their obligations before shipping a model to users.
|
|
41
|
+
*/
|
|
42
|
+
license: string;
|
|
36
43
|
};
|
|
37
44
|
|
|
38
45
|
export const MODEL_REGISTRY: ModelRegistryEntry[] = [
|
|
@@ -50,6 +57,7 @@ export const MODEL_REGISTRY: ModelRegistryEntry[] = [
|
|
|
50
57
|
contextWindow: 8_000,
|
|
51
58
|
minRamBytes: 2_000_000_000, // 2GB — LiteRT-LM memory-maps weights, actual RSS ~1.5GB
|
|
52
59
|
supportedPlatforms: ['ios', 'android'],
|
|
60
|
+
license: 'Gemma',
|
|
53
61
|
},
|
|
54
62
|
{
|
|
55
63
|
id: 'gemma-e4b',
|
|
@@ -63,13 +71,253 @@ export const MODEL_REGISTRY: ModelRegistryEntry[] = [
|
|
|
63
71
|
contextWindow: 16_000,
|
|
64
72
|
minRamBytes: 3_000_000_000, // 3GB — LiteRT-LM memory-maps weights
|
|
65
73
|
supportedPlatforms: ['ios', 'android'],
|
|
74
|
+
license: 'Gemma',
|
|
75
|
+
},
|
|
76
|
+
// --- Qwen3 (Apache-2.0) — official litert-community builds. A size ladder
|
|
77
|
+
// from a sub-GB model that runs anywhere up to a 4B that rivals Gemma E4B. ---
|
|
78
|
+
{
|
|
79
|
+
id: 'qwen3-0.6b',
|
|
80
|
+
name: 'Qwen3 0.6B',
|
|
81
|
+
parameterCount: '0.6B',
|
|
82
|
+
quantization: 'mixed-int4',
|
|
83
|
+
downloadUrl:
|
|
84
|
+
'https://huggingface.co/litert-community/Qwen3-0.6B/resolve/main/qwen3_0_6b_mixed_int4.litertlm',
|
|
85
|
+
sha256: 'b1baab462f6be49d70eada79d715c2c52cd9ece0cad00bddf6a2c097d23498e9',
|
|
86
|
+
sizeBytes: 497_664_000, // 475MB (exact, HF LFS)
|
|
87
|
+
// The int4 build ships a 2048-token KV. TODO: benchmark on device.
|
|
88
|
+
contextWindow: 2_048,
|
|
89
|
+
minRamBytes: 1_000_000_000, // 1GB — tiny; runs on virtually any modern device
|
|
90
|
+
supportedPlatforms: ['ios', 'android'],
|
|
91
|
+
license: 'Apache-2.0',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'qwen3-1.7b',
|
|
95
|
+
name: 'Qwen3 1.7B',
|
|
96
|
+
parameterCount: '1.7B',
|
|
97
|
+
quantization: 'dynamic-int8',
|
|
98
|
+
downloadUrl:
|
|
99
|
+
'https://huggingface.co/litert-community/Qwen3-1.7B/resolve/main/Qwen3_1.7B.litertlm',
|
|
100
|
+
sha256: '66064a4e9269cb693e124c4e3040bcb8a446b10bca42663896329495add3861c',
|
|
101
|
+
sizeBytes: 2_056_729_520, // 2.06GB (exact, HF LFS)
|
|
102
|
+
// Conservative default (not marked in the filename). TODO: benchmark on device.
|
|
103
|
+
contextWindow: 4_096,
|
|
104
|
+
minRamBytes: 2_000_000_000, // 2GB
|
|
105
|
+
supportedPlatforms: ['ios', 'android'],
|
|
106
|
+
license: 'Apache-2.0',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'qwen3-4b',
|
|
110
|
+
name: 'Qwen3 4B',
|
|
111
|
+
parameterCount: '4B',
|
|
112
|
+
quantization: 'mixed-int4',
|
|
113
|
+
downloadUrl:
|
|
114
|
+
'https://huggingface.co/litert-community/Qwen3-4B/resolve/main/qwen3_4b_mixed_int4.litertlm',
|
|
115
|
+
sha256: 'f0794bc77efeaaf4f7af815f04c483b19b8f2ae4a102cef1b7b760a25848a18e',
|
|
116
|
+
sizeBytes: 2_659_057_664, // 2.66GB (exact, HF LFS)
|
|
117
|
+
// Conservative default (not marked in the filename). TODO: benchmark on device.
|
|
118
|
+
contextWindow: 4_096,
|
|
119
|
+
minRamBytes: 3_000_000_000, // 3GB — 4B params need more headroom than the similarly-sized E2B
|
|
120
|
+
supportedPlatforms: ['ios', 'android'],
|
|
121
|
+
license: 'Apache-2.0',
|
|
122
|
+
},
|
|
123
|
+
// --- Phi-4 Mini (MIT) — strong reasoning; q8 build, the heaviest downloadable. ---
|
|
124
|
+
{
|
|
125
|
+
id: 'phi-4-mini',
|
|
126
|
+
name: 'Phi-4 Mini',
|
|
127
|
+
parameterCount: '3.8B',
|
|
128
|
+
quantization: 'int8',
|
|
129
|
+
downloadUrl:
|
|
130
|
+
'https://huggingface.co/litert-community/Phi-4-mini-instruct/resolve/main/Phi-4-mini-instruct_multi-prefill-seq_q8_ekv4096.litertlm',
|
|
131
|
+
sha256: '7764d4deb53800578307be33039476b38a6c370fff71bedb3c0552563e23ab02',
|
|
132
|
+
sizeBytes: 3_910_090_752, // 3.91GB (exact, HF LFS)
|
|
133
|
+
contextWindow: 4_096, // ekv4096 build
|
|
134
|
+
minRamBytes: 4_000_000_000, // 4GB — q8 weights, heaviest downloadable
|
|
135
|
+
supportedPlatforms: ['ios', 'android'],
|
|
136
|
+
license: 'MIT',
|
|
66
137
|
},
|
|
67
138
|
];
|
|
68
139
|
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Custom (developer-registered) models — "bring your own model".
|
|
142
|
+
//
|
|
143
|
+
// The built-in MODEL_REGISTRY above is curated: each entry's SHA256 is pinned by
|
|
144
|
+
// a maintainer who verified the bytes. registerModel() lets app developers add
|
|
145
|
+
// any LiteRT-LM model under the same contract — they supply the metadata
|
|
146
|
+
// (including the SHA256), so the integrity check still holds end-to-end. Custom
|
|
147
|
+
// entries live in memory only; call registerModel() at startup on every launch
|
|
148
|
+
// (the downloaded file on disk persists and is keyed by id, so status survives
|
|
149
|
+
// restarts once you re-register).
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
const customModels = new Map<string, ModelRegistryEntry>();
|
|
153
|
+
|
|
154
|
+
/** Ids owned by the native built-in backends; not valid for custom models. */
|
|
155
|
+
const RESERVED_MODEL_IDS = new Set(['apple-fm', 'mlkit']);
|
|
156
|
+
|
|
157
|
+
const SHA256_RE = /^[a-f0-9]{64}$/i;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Validate a model entry, returning a list of human-readable problems
|
|
161
|
+
* (empty ⇒ valid). Pure — used by {@link registerModel} and unit-tested.
|
|
162
|
+
*/
|
|
163
|
+
export function validateModelEntry(entry: ModelRegistryEntry): string[] {
|
|
164
|
+
const errors: string[] = [];
|
|
165
|
+
if (!entry || typeof entry !== 'object') return ['entry must be an object'];
|
|
166
|
+
if (!entry.id || typeof entry.id !== 'string') errors.push('id is required');
|
|
167
|
+
if (!entry.name || typeof entry.name !== 'string') errors.push('name is required');
|
|
168
|
+
if (!entry.parameterCount) errors.push('parameterCount is required (e.g. "1.7B")');
|
|
169
|
+
if (!entry.quantization) errors.push('quantization is required (e.g. "int4")');
|
|
170
|
+
if (typeof entry.downloadUrl !== 'string' || !/^https?:\/\//.test(entry.downloadUrl)) {
|
|
171
|
+
errors.push('downloadUrl must be an http(s) URL');
|
|
172
|
+
}
|
|
173
|
+
if (typeof entry.sha256 !== 'string' || !SHA256_RE.test(entry.sha256)) {
|
|
174
|
+
errors.push('sha256 must be a 64-character hex string (use fetchModelMetadata to obtain it)');
|
|
175
|
+
}
|
|
176
|
+
if (!Number.isFinite(entry.sizeBytes) || entry.sizeBytes <= 0) {
|
|
177
|
+
errors.push('sizeBytes must be a positive number');
|
|
178
|
+
}
|
|
179
|
+
if (!Number.isFinite(entry.minRamBytes) || entry.minRamBytes < 0) {
|
|
180
|
+
errors.push('minRamBytes must be >= 0');
|
|
181
|
+
}
|
|
182
|
+
if (!Number.isInteger(entry.contextWindow) || entry.contextWindow <= 0) {
|
|
183
|
+
errors.push('contextWindow must be a positive integer');
|
|
184
|
+
}
|
|
185
|
+
if (!Array.isArray(entry.supportedPlatforms) || entry.supportedPlatforms.length === 0) {
|
|
186
|
+
errors.push('supportedPlatforms must list at least one of "ios" / "android"');
|
|
187
|
+
} else if (!entry.supportedPlatforms.every((p) => p === 'ios' || p === 'android')) {
|
|
188
|
+
errors.push('supportedPlatforms may only contain "ios" or "android"');
|
|
189
|
+
}
|
|
190
|
+
if (!entry.license || typeof entry.license !== 'string') {
|
|
191
|
+
errors.push('license is required (e.g. "Apache-2.0", "MIT")');
|
|
192
|
+
}
|
|
193
|
+
return errors;
|
|
194
|
+
}
|
|
195
|
+
|
|
69
196
|
/**
|
|
70
|
-
*
|
|
197
|
+
* Register a custom downloadable model at runtime.
|
|
198
|
+
*
|
|
199
|
+
* After registering, the id works with `downloadModel` / `setModel` /
|
|
200
|
+
* `getDownloadableModels` exactly like a built-in. The download is integrity-
|
|
201
|
+
* checked against the `sha256` you provide — pin a value you trust (see
|
|
202
|
+
* {@link fetchModelMetadata}). Throws if the entry is invalid or the id
|
|
203
|
+
* collides with a built-in (curated or native) model.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* const { sha256, sizeBytes } = await fetchModelMetadata(url); // dev-time
|
|
208
|
+
* registerModel({
|
|
209
|
+
* id: 'qwen3-8b', name: 'Qwen3 8B', parameterCount: '8B', quantization: 'int4',
|
|
210
|
+
* downloadUrl: url, sha256, sizeBytes,
|
|
211
|
+
* contextWindow: 4096, minRamBytes: 6_000_000_000,
|
|
212
|
+
* supportedPlatforms: ['ios', 'android'], license: 'Apache-2.0',
|
|
213
|
+
* });
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
export function registerModel(entry: ModelRegistryEntry): void {
|
|
217
|
+
const errors = validateModelEntry(entry);
|
|
218
|
+
if (errors.length > 0) {
|
|
219
|
+
throw new Error(`registerModel: invalid model entry — ${errors.join('; ')}`);
|
|
220
|
+
}
|
|
221
|
+
if (RESERVED_MODEL_IDS.has(entry.id) || MODEL_REGISTRY.some((m) => m.id === entry.id)) {
|
|
222
|
+
throw new Error(`registerModel: "${entry.id}" is a built-in model id; choose a different id`);
|
|
223
|
+
}
|
|
224
|
+
// Clone so later external mutation of the caller's object can't corrupt the registry.
|
|
225
|
+
customModels.set(entry.id, { ...entry, supportedPlatforms: [...entry.supportedPlatforms] });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Remove a previously {@link registerModel}'d custom model.
|
|
230
|
+
* Returns true if one was removed. Does not delete any downloaded file —
|
|
231
|
+
* use `deleteModel` for that. No-op for built-in models.
|
|
232
|
+
*/
|
|
233
|
+
export function unregisterModel(modelId: string): boolean {
|
|
234
|
+
return customModels.delete(modelId);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** All custom models registered via {@link registerModel}, in registration order. */
|
|
238
|
+
export function getRegisteredModels(): ModelRegistryEntry[] {
|
|
239
|
+
return [...customModels.values()];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Built-in registry plus all custom models. */
|
|
243
|
+
export function getAllModels(): ModelRegistryEntry[] {
|
|
244
|
+
return [...MODEL_REGISTRY, ...customModels.values()];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Look up a model registry entry by ID (built-in or custom).
|
|
71
249
|
* Returns undefined if not found.
|
|
72
250
|
*/
|
|
73
251
|
export function getRegistryEntry(modelId: string): ModelRegistryEntry | undefined {
|
|
74
|
-
return MODEL_REGISTRY.find((m) => m.id === modelId);
|
|
252
|
+
return MODEL_REGISTRY.find((m) => m.id === modelId) ?? customModels.get(modelId);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Parse a HuggingFace "resolve" download URL into its parts.
|
|
257
|
+
* Returns null if the URL isn't a HuggingFace resolve URL. Pure — unit-tested.
|
|
258
|
+
*
|
|
259
|
+
* e.g. https://huggingface.co/litert-community/Qwen3-0.6B/resolve/main/model.litertlm
|
|
260
|
+
* → { repo: 'litert-community/Qwen3-0.6B', revision: 'main', path: 'model.litertlm' }
|
|
261
|
+
*/
|
|
262
|
+
export function parseHuggingFaceUrl(
|
|
263
|
+
url: string
|
|
264
|
+
): { repo: string; revision: string; path: string } | null {
|
|
265
|
+
if (typeof url !== 'string') return null;
|
|
266
|
+
const clean = url.split('#')[0].split('?')[0];
|
|
267
|
+
const m = /^https?:\/\/huggingface\.co\/([^/]+\/[^/]+)\/resolve\/([^/]+)\/(.+)$/.exec(clean);
|
|
268
|
+
if (!m) return null;
|
|
269
|
+
return { repo: m[1], revision: m[2], path: decodeURIComponent(m[3]) };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Look up a model file's SHA256 and byte size from HuggingFace, so you can fill
|
|
274
|
+
* in a {@link registerModel} entry without computing them by hand.
|
|
275
|
+
*
|
|
276
|
+
* Trust note: this reads the hash from the same host you'll download from, so it
|
|
277
|
+
* only guards against download corruption — NOT a maliciously changed upstream
|
|
278
|
+
* repo. For a real supply-chain guarantee, run this once at dev time and PIN the
|
|
279
|
+
* returned `sha256` in your source, exactly like the built-in registry.
|
|
280
|
+
*
|
|
281
|
+
* @param downloadUrl - A HuggingFace resolve URL (the one you'll register).
|
|
282
|
+
* @returns `{ sha256, sizeBytes }` ready to spread into a registry entry.
|
|
283
|
+
* @throws if the URL isn't a HuggingFace resolve URL, the API call fails, or the
|
|
284
|
+
* file isn't an LFS object (no hash/size available).
|
|
285
|
+
*/
|
|
286
|
+
export async function fetchModelMetadata(
|
|
287
|
+
downloadUrl: string
|
|
288
|
+
): Promise<{ sha256: string; sizeBytes: number }> {
|
|
289
|
+
const parsed = parseHuggingFaceUrl(downloadUrl);
|
|
290
|
+
if (!parsed) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
'fetchModelMetadata: expected a HuggingFace resolve URL ' +
|
|
293
|
+
'(https://huggingface.co/<owner>/<repo>/resolve/<revision>/<file>)'
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
const { repo, revision, path } = parsed;
|
|
297
|
+
const slash = path.lastIndexOf('/');
|
|
298
|
+
const dir = slash >= 0 ? path.slice(0, slash) : '';
|
|
299
|
+
const treeUrl =
|
|
300
|
+
`https://huggingface.co/api/models/${repo}/tree/${revision}` + (dir ? `/${dir}` : '');
|
|
301
|
+
|
|
302
|
+
const res = await fetch(treeUrl);
|
|
303
|
+
if (!res.ok) {
|
|
304
|
+
throw new Error(`fetchModelMetadata: HuggingFace API returned ${res.status} for ${treeUrl}`);
|
|
305
|
+
}
|
|
306
|
+
const items = (await res.json()) as Array<{
|
|
307
|
+
path?: string;
|
|
308
|
+
size?: number;
|
|
309
|
+
lfs?: { oid?: string; size?: number };
|
|
310
|
+
}>;
|
|
311
|
+
const file = Array.isArray(items) ? items.find((it) => it?.path === path) : undefined;
|
|
312
|
+
if (!file) {
|
|
313
|
+
throw new Error(`fetchModelMetadata: "${path}" not found in ${repo}@${revision}`);
|
|
314
|
+
}
|
|
315
|
+
const sha256 = file.lfs?.oid;
|
|
316
|
+
const sizeBytes = file.lfs?.size ?? file.size;
|
|
317
|
+
if (!sha256 || !sizeBytes) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`fetchModelMetadata: "${path}" has no LFS hash/size — is it the actual model weight file?`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
return { sha256, sizeBytes };
|
|
75
323
|
}
|
package/src/types.ts
CHANGED
|
@@ -375,6 +375,8 @@ export type DownloadableModel = {
|
|
|
375
375
|
name: string;
|
|
376
376
|
/** Parameter count label (e.g. '2.3B') */
|
|
377
377
|
parameterCount: string;
|
|
378
|
+
/** License the weights are distributed under (e.g. 'Apache-2.0', 'MIT', 'Gemma'). */
|
|
379
|
+
license: string;
|
|
378
380
|
/** Download file size in bytes */
|
|
379
381
|
sizeBytes: number;
|
|
380
382
|
/** Maximum context window in tokens */
|