expo-ai-kit 0.4.0 → 0.5.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 +28 -96
- package/android/src/main/java/expo/modules/aikit/ExpoAiKitModule.kt +26 -13
- package/android/src/main/java/expo/modules/aikit/GemmaInferenceClient.kt +47 -2
- package/build/ExpoAiKitModule.d.ts +12 -3
- package/build/ExpoAiKitModule.d.ts.map +1 -1
- package/build/ExpoAiKitModule.js.map +1 -1
- package/build/index.d.ts +31 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +239 -34
- package/build/index.js.map +1 -1
- package/build/models.js +6 -6
- package/build/models.js.map +1 -1
- package/build/types.d.ts +66 -2
- package/build/types.d.ts.map +1 -1
- package/build/types.js.map +1 -1
- package/ios/ExpoAiKit.podspec +11 -5
- package/ios/ExpoAiKitModule.swift +255 -95
- package/ios/GemmaInferenceClient.swift +408 -0
- package/ios/Vendor/LiteRTLM/Benchmark.swift +83 -0
- package/ios/Vendor/LiteRTLM/Capabilities.swift +41 -0
- package/ios/Vendor/LiteRTLM/Config.swift +172 -0
- package/ios/Vendor/LiteRTLM/Conversation.swift +450 -0
- package/ios/Vendor/LiteRTLM/Engine.swift +208 -0
- package/ios/Vendor/LiteRTLM/ExperimentalFlags.swift +142 -0
- package/ios/Vendor/LiteRTLM/LICENSE +201 -0
- package/ios/Vendor/LiteRTLM/LiteRTLMError.swift +156 -0
- package/ios/Vendor/LiteRTLM/Message.swift +225 -0
- package/ios/Vendor/LiteRTLM/Tool.swift +291 -0
- package/ios/Vendor/LiteRTLM/ToolManager.swift +152 -0
- package/package.json +7 -3
- package/scripts/install-litertlm.sh +63 -0
- package/src/ExpoAiKitModule.ts +25 -3
- package/src/index.ts +287 -58
- package/src/models.ts +6 -6
- package/src/types.ts +72 -1
- package/android/build/.transforms/05637efea134638df3d3fc7b19d5dfdb/results.bin +0 -1
- package/android/build/.transforms/05637efea134638df3d3fc7b19d5dfdb/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/.transforms/cd4ecc077e8795b855097bce5bf059b1/results.bin +0 -1
- package/android/build/.transforms/cd4ecc077e8795b855097bce5bf059b1/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/generated/source/buildConfig/debug/expo/modules/aikit/BuildConfig.java +0 -10
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +0 -7
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +0 -18
- package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +0 -6
- package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +0 -1
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
- package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +0 -1
- package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +0 -2
- package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +0 -2
- package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +0 -2
- package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +0 -2
- package/android/build/intermediates/java_res/debug/processDebugJavaRes/out/META-INF/expo-ai-kit_debug.kotlin_module +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/expo/modules/aikit/BuildConfig.class +0 -0
- package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +0 -2
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +0 -7
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +0 -7
- package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +0 -1
- package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +0 -1
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +0 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +0 -2
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +0 -14
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build/tmp/kotlin-classes/debug/META-INF/expo-ai-kit_debug.kotlin_module +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$1$11$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$1$2$conversationPrompt$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$1$3$conversationPrompt$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$1$3$job$1$streamCallback$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$1$3$job$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$AsyncFunction$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$AsyncFunction$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$AsyncFunction$3.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$AsyncFunction$4.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$AsyncFunction$5.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$AsyncFunction$6.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$AsyncFunction$7.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$10.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$11.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$12.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$3.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$4.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$5.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$6.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$7.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$8.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Coroutine$9.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Function$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$Function$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$FunctionWithoutArgs$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$FunctionWithoutArgs$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$FunctionWithoutArgs$3.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule$definition$lambda$20$$inlined$FunctionWithoutArgs$4.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/ExpoAiKitModule.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$deleteModelFile$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$downloadModelFile$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$downloadModelFile$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$generateText$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$generateText$2$1$1$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$generateText$2$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$generateTextStream$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$generateTextStream$2$1$1$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$generateTextStream$2$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$loadModel$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$loadModel$2$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient$unloadModel$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/GemmaInferenceClient.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/PromptApiClient$generateText$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/PromptApiClient$generateTextStream$2$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/PromptApiClient$generateTextStream$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/PromptApiClient$isAvailable$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/PromptApiClient$isAvailableBlocking$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/expo/modules/aikit/PromptApiClient.class +0 -0
- package/ios/.xcode.env +0 -11
package/build/index.js
CHANGED
|
@@ -7,7 +7,104 @@ export * from './models';
|
|
|
7
7
|
const DEFAULT_SYSTEM_PROMPT = 'You are a helpful, friendly assistant. Answer the user directly and concisely.';
|
|
8
8
|
let streamIdCounter = 0;
|
|
9
9
|
function generateSessionId() {
|
|
10
|
-
return `
|
|
10
|
+
return `gen_${Date.now()}_${++streamIdCounter}`;
|
|
11
|
+
}
|
|
12
|
+
// The set of codes the native layer encodes in error messages as "CODE:modelId:reason".
|
|
13
|
+
const KNOWN_ERROR_CODES = new Set([
|
|
14
|
+
'MODEL_NOT_FOUND',
|
|
15
|
+
'MODEL_NOT_DOWNLOADED',
|
|
16
|
+
'DOWNLOAD_FAILED',
|
|
17
|
+
'DOWNLOAD_CORRUPT',
|
|
18
|
+
'DOWNLOAD_STORAGE_FULL',
|
|
19
|
+
'DOWNLOAD_CANCELLED',
|
|
20
|
+
'INFERENCE_OOM',
|
|
21
|
+
'INFERENCE_FAILED',
|
|
22
|
+
'INFERENCE_BUSY',
|
|
23
|
+
'INFERENCE_CANCELLED',
|
|
24
|
+
'MODEL_LOAD_FAILED',
|
|
25
|
+
'DEVICE_NOT_SUPPORTED',
|
|
26
|
+
]);
|
|
27
|
+
/**
|
|
28
|
+
* Normalize an error from the native layer into a {@link ModelError}.
|
|
29
|
+
*
|
|
30
|
+
* The native modules format failures as "CODE:modelId:reason" (see the
|
|
31
|
+
* GemmaError/GemmaInferenceClient contract). Expo surfaces that string as the
|
|
32
|
+
* error's message, so we parse it here and rethrow a typed ModelError with a
|
|
33
|
+
* reliable `.code` and `.modelId`. Anything unrecognized becomes UNKNOWN.
|
|
34
|
+
*/
|
|
35
|
+
function toModelError(e) {
|
|
36
|
+
if (e instanceof ModelError)
|
|
37
|
+
throw e;
|
|
38
|
+
const message = String(e?.message ?? e ?? '');
|
|
39
|
+
const match = /^([A-Z_]+):([^:]*):([\s\S]*)$/.exec(message);
|
|
40
|
+
if (match && KNOWN_ERROR_CODES.has(match[1])) {
|
|
41
|
+
throw new ModelError(match[1], match[2], match[3]);
|
|
42
|
+
}
|
|
43
|
+
throw new ModelError('UNKNOWN', '', message);
|
|
44
|
+
}
|
|
45
|
+
/** Run a native promise, normalizing any rejection into a ModelError. */
|
|
46
|
+
async function wrapNative(run) {
|
|
47
|
+
try {
|
|
48
|
+
return await run();
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
toModelError(e);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Single-flight inference guard
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// On-device models are backed by a single native context + KV cache that is not
|
|
58
|
+
// safe for concurrent decodes (interleaving can corrupt the cache and crash the
|
|
59
|
+
// native side). JS is single-threaded, so a synchronous check-and-set of this
|
|
60
|
+
// flag before any `await` is race-free. The flag is shared by sendMessage and
|
|
61
|
+
// streamMessage and is held until the *native* call settles — not until an
|
|
62
|
+
// early abort — so a detached-but-still-running generation still blocks a new one.
|
|
63
|
+
let inferenceInFlight = false;
|
|
64
|
+
function acquireInference() {
|
|
65
|
+
if (inferenceInFlight) {
|
|
66
|
+
throw new ModelError('INFERENCE_BUSY', '', 'A generation is already in flight. Wait for it to finish, or stop the active stream first.');
|
|
67
|
+
}
|
|
68
|
+
inferenceInFlight = true;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Map the public GenerationConfig to the native shape, dropping undefined fields
|
|
72
|
+
* and validating ranges up front so callers get a clear error instead of an
|
|
73
|
+
* opaque native MODEL_LOAD_FAILED from the sampler.
|
|
74
|
+
*/
|
|
75
|
+
function toNativeGeneration(g) {
|
|
76
|
+
const out = {};
|
|
77
|
+
if (g?.temperature != null) {
|
|
78
|
+
if (g.temperature < 0) {
|
|
79
|
+
throw new Error('generation.temperature must be >= 0');
|
|
80
|
+
}
|
|
81
|
+
out.temperature = g.temperature;
|
|
82
|
+
}
|
|
83
|
+
if (g?.topK != null) {
|
|
84
|
+
if (!Number.isInteger(g.topK) || g.topK <= 0) {
|
|
85
|
+
throw new Error('generation.topK must be a positive integer');
|
|
86
|
+
}
|
|
87
|
+
out.topK = g.topK;
|
|
88
|
+
}
|
|
89
|
+
if (g?.topP != null) {
|
|
90
|
+
if (g.topP < 0 || g.topP > 1) {
|
|
91
|
+
throw new Error('generation.topP must be within [0, 1]');
|
|
92
|
+
}
|
|
93
|
+
out.topP = g.topP;
|
|
94
|
+
}
|
|
95
|
+
if (g?.seed != null) {
|
|
96
|
+
if (!Number.isInteger(g.seed)) {
|
|
97
|
+
throw new Error('generation.seed must be an integer');
|
|
98
|
+
}
|
|
99
|
+
out.seed = g.seed;
|
|
100
|
+
}
|
|
101
|
+
if (g?.maxTokens != null) {
|
|
102
|
+
if (!Number.isInteger(g.maxTokens) || g.maxTokens <= 0) {
|
|
103
|
+
throw new Error('generation.maxTokens must be a positive integer');
|
|
104
|
+
}
|
|
105
|
+
out.maxTokens = g.maxTokens;
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
11
108
|
}
|
|
12
109
|
// ============================================================================
|
|
13
110
|
// Inference API
|
|
@@ -64,12 +161,58 @@ export async function sendMessage(messages, options) {
|
|
|
64
161
|
if (!messages || messages.length === 0) {
|
|
65
162
|
throw new Error('messages array cannot be empty');
|
|
66
163
|
}
|
|
164
|
+
if (options?.signal?.aborted) {
|
|
165
|
+
throw new ModelError('INFERENCE_CANCELLED', '', 'Aborted before start');
|
|
166
|
+
}
|
|
67
167
|
// Determine system prompt: use from messages array if present, else options, else default
|
|
68
168
|
const hasSystemMessage = messages.some((m) => m.role === 'system');
|
|
69
169
|
const systemPrompt = hasSystemMessage
|
|
70
170
|
? '' // Native will extract from messages
|
|
71
171
|
: options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
72
|
-
|
|
172
|
+
acquireInference(); // throws INFERENCE_BUSY if a generation is already running
|
|
173
|
+
const sessionId = generateSessionId();
|
|
174
|
+
// Hold the single-flight flag until the NATIVE call settles — even if the
|
|
175
|
+
// caller aborts early — because the model may keep computing in the background.
|
|
176
|
+
const native = ExpoAiKitModule.sendMessage(messages, systemPrompt, sessionId);
|
|
177
|
+
const release = () => {
|
|
178
|
+
inferenceInFlight = false;
|
|
179
|
+
};
|
|
180
|
+
native.then(release, release);
|
|
181
|
+
const signal = options?.signal;
|
|
182
|
+
if (!signal) {
|
|
183
|
+
try {
|
|
184
|
+
return await native;
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
toModelError(e);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Race the native result against the abort signal. On abort we unblock the
|
|
191
|
+
// caller immediately and best-effort ask native to cancel; the flag stays
|
|
192
|
+
// held (via `release` above) until the native call actually finishes.
|
|
193
|
+
return await new Promise((resolve, reject) => {
|
|
194
|
+
let done = false;
|
|
195
|
+
const finish = (action) => {
|
|
196
|
+
if (done)
|
|
197
|
+
return;
|
|
198
|
+
done = true;
|
|
199
|
+
signal.removeEventListener('abort', onAbort);
|
|
200
|
+
action();
|
|
201
|
+
};
|
|
202
|
+
function onAbort() {
|
|
203
|
+
ExpoAiKitModule.stopStreaming(sessionId).catch(() => { });
|
|
204
|
+
finish(() => reject(new ModelError('INFERENCE_CANCELLED', '', 'Aborted by caller')));
|
|
205
|
+
}
|
|
206
|
+
signal.addEventListener('abort', onAbort);
|
|
207
|
+
native.then((r) => finish(() => resolve(r)), (e) => finish(() => {
|
|
208
|
+
try {
|
|
209
|
+
toModelError(e);
|
|
210
|
+
}
|
|
211
|
+
catch (me) {
|
|
212
|
+
reject(me);
|
|
213
|
+
}
|
|
214
|
+
}));
|
|
215
|
+
});
|
|
73
216
|
}
|
|
74
217
|
/**
|
|
75
218
|
* Stream messages to the on-device LLM and receive progressive token updates.
|
|
@@ -118,42 +261,60 @@ export function streamMessage(messages, onToken, options) {
|
|
|
118
261
|
stop: () => { },
|
|
119
262
|
};
|
|
120
263
|
}
|
|
264
|
+
if (inferenceInFlight) {
|
|
265
|
+
return {
|
|
266
|
+
promise: Promise.reject(new ModelError('INFERENCE_BUSY', '', 'A generation is already in flight. Stop the active stream first.')),
|
|
267
|
+
stop: () => { },
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
inferenceInFlight = true; // set synchronously — race-free with other JS
|
|
121
271
|
const sessionId = generateSessionId();
|
|
122
|
-
let finalText = '';
|
|
123
|
-
let stopped = false;
|
|
124
272
|
// Determine system prompt: use from messages array if present, else options, else default
|
|
125
273
|
const hasSystemMessage = messages.some((m) => m.role === 'system');
|
|
126
274
|
const systemPrompt = hasSystemMessage
|
|
127
275
|
? '' // Native will extract from messages
|
|
128
276
|
: options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
277
|
+
let finalText = '';
|
|
278
|
+
let settled = false;
|
|
279
|
+
let subscription;
|
|
280
|
+
let resolveOuter;
|
|
281
|
+
let rejectOuter;
|
|
282
|
+
// Settle exactly once: remove the listener and release the single-flight flag.
|
|
283
|
+
const settle = (action) => {
|
|
284
|
+
if (settled)
|
|
285
|
+
return;
|
|
286
|
+
settled = true;
|
|
287
|
+
subscription?.remove();
|
|
288
|
+
inferenceInFlight = false;
|
|
289
|
+
action();
|
|
290
|
+
};
|
|
129
291
|
const promise = new Promise((resolve, reject) => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
292
|
+
resolveOuter = resolve;
|
|
293
|
+
rejectOuter = reject;
|
|
294
|
+
});
|
|
295
|
+
subscription = ExpoAiKitModule.addListener('onStreamToken', (event) => {
|
|
296
|
+
if (event.sessionId !== sessionId)
|
|
297
|
+
return;
|
|
298
|
+
finalText = event.accumulatedText;
|
|
299
|
+
onToken(event);
|
|
300
|
+
if (event.isDone)
|
|
301
|
+
settle(() => resolveOuter({ text: finalText }));
|
|
302
|
+
});
|
|
303
|
+
ExpoAiKitModule.startStreaming(messages, systemPrompt, sessionId).catch((error) => {
|
|
304
|
+
settle(() => {
|
|
305
|
+
try {
|
|
306
|
+
toModelError(error);
|
|
307
|
+
}
|
|
308
|
+
catch (me) {
|
|
309
|
+
rejectOuter(me);
|
|
142
310
|
}
|
|
143
|
-
});
|
|
144
|
-
// Start streaming on native side
|
|
145
|
-
ExpoAiKitModule.startStreaming(messages, systemPrompt, sessionId).catch((error) => {
|
|
146
|
-
subscription.remove();
|
|
147
|
-
reject(error);
|
|
148
311
|
});
|
|
149
312
|
});
|
|
150
313
|
const stop = () => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// Ignore errors when stopping
|
|
156
|
-
});
|
|
314
|
+
// Best-effort native cancel (native also emits a terminal isDone on cancel),
|
|
315
|
+
// but resolve immediately with the text so far so `promise` can never hang.
|
|
316
|
+
ExpoAiKitModule.stopStreaming(sessionId).catch(() => { });
|
|
317
|
+
settle(() => resolveOuter({ text: finalText }));
|
|
157
318
|
};
|
|
158
319
|
return { promise, stop };
|
|
159
320
|
}
|
|
@@ -194,8 +355,10 @@ export async function getDownloadableModels() {
|
|
|
194
355
|
catch {
|
|
195
356
|
// Native call unavailable -- default to 0 (all models will show meetsRequirements: false)
|
|
196
357
|
}
|
|
197
|
-
return platformModels.map((entry) => {
|
|
198
|
-
|
|
358
|
+
return Promise.all(platformModels.map(async (entry) => {
|
|
359
|
+
// Await: on iOS this bridges as a Promise (reads actor state); on Android
|
|
360
|
+
// it's synchronous and awaiting a plain value is a no-op.
|
|
361
|
+
const status = await ExpoAiKitModule.getDownloadableModelStatus(entry.id);
|
|
199
362
|
return {
|
|
200
363
|
id: entry.id,
|
|
201
364
|
name: entry.name,
|
|
@@ -206,7 +369,34 @@ export async function getDownloadableModels() {
|
|
|
206
369
|
meetsRequirements: deviceRamBytes >= entry.minRamBytes,
|
|
207
370
|
status,
|
|
208
371
|
};
|
|
209
|
-
});
|
|
372
|
+
}));
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Pick the best downloadable model the current device can run.
|
|
376
|
+
*
|
|
377
|
+
* Returns the most capable model (largest, by RAM requirement) whose
|
|
378
|
+
* `meetsRequirements` is true — e.g. Gemma 4 E4B on high-spec phones, falling
|
|
379
|
+
* back to E2B on more constrained ones — or `null` if the device can't run any.
|
|
380
|
+
*
|
|
381
|
+
* This is a convenience over {@link getDownloadableModels}; the caller still
|
|
382
|
+
* downloads + activates explicitly. Pass `platform` is implicit (current OS).
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```ts
|
|
386
|
+
* const best = await getRecommendedModel();
|
|
387
|
+
* if (best) {
|
|
388
|
+
* await downloadModel(best.id, { onProgress });
|
|
389
|
+
* await setModel(best.id);
|
|
390
|
+
* }
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
export async function getRecommendedModel() {
|
|
394
|
+
const models = await getDownloadableModels();
|
|
395
|
+
const runnable = models.filter((m) => m.meetsRequirements);
|
|
396
|
+
if (runnable.length === 0)
|
|
397
|
+
return null;
|
|
398
|
+
// Higher RAM requirement ⇒ larger/more capable model. Prefer the biggest that fits.
|
|
399
|
+
return runnable.sort((a, b) => b.minRamBytes - a.minRamBytes)[0];
|
|
210
400
|
}
|
|
211
401
|
/**
|
|
212
402
|
* Download a model to the device.
|
|
@@ -251,12 +441,26 @@ export async function downloadModel(modelId, options) {
|
|
|
251
441
|
});
|
|
252
442
|
}
|
|
253
443
|
try {
|
|
254
|
-
await ExpoAiKitModule.downloadModel(modelId, entry.downloadUrl, entry.sha256);
|
|
444
|
+
await wrapNative(() => ExpoAiKitModule.downloadModel(modelId, entry.downloadUrl, entry.sha256));
|
|
255
445
|
}
|
|
256
446
|
finally {
|
|
257
447
|
subscription?.remove();
|
|
258
448
|
}
|
|
259
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* Cancel an in-flight download for a model.
|
|
452
|
+
*
|
|
453
|
+
* The in-progress {@link downloadModel} promise rejects with a
|
|
454
|
+
* DOWNLOAD_CANCELLED {@link ModelError}. No-op if the model isn't downloading.
|
|
455
|
+
*
|
|
456
|
+
* @param modelId - ID of the model whose download should be cancelled
|
|
457
|
+
*/
|
|
458
|
+
export async function cancelDownload(modelId) {
|
|
459
|
+
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
await wrapNative(() => ExpoAiKitModule.cancelDownload(modelId));
|
|
463
|
+
}
|
|
260
464
|
/**
|
|
261
465
|
* Delete a downloaded model from the device.
|
|
262
466
|
*
|
|
@@ -270,7 +474,7 @@ export async function deleteModel(modelId) {
|
|
|
270
474
|
if (!entry) {
|
|
271
475
|
throw new ModelError('MODEL_NOT_FOUND', modelId);
|
|
272
476
|
}
|
|
273
|
-
await ExpoAiKitModule.deleteModel(modelId);
|
|
477
|
+
await wrapNative(() => ExpoAiKitModule.deleteModel(modelId));
|
|
274
478
|
}
|
|
275
479
|
/**
|
|
276
480
|
* Set the active model for inference.
|
|
@@ -299,7 +503,8 @@ export async function setModel(modelId, options) {
|
|
|
299
503
|
const entry = getRegistryEntry(modelId);
|
|
300
504
|
const minRamBytes = entry?.minRamBytes ?? 0;
|
|
301
505
|
const backend = options?.backend ?? 'auto';
|
|
302
|
-
|
|
506
|
+
const generation = toNativeGeneration(options?.generation);
|
|
507
|
+
await wrapNative(() => ExpoAiKitModule.setModel(modelId, minRamBytes, backend, generation));
|
|
303
508
|
}
|
|
304
509
|
/**
|
|
305
510
|
* Get the ID of the currently active model.
|
|
@@ -316,6 +521,6 @@ export function getActiveModel() {
|
|
|
316
521
|
* No-op if no downloadable model is currently loaded.
|
|
317
522
|
*/
|
|
318
523
|
export async function unloadModel() {
|
|
319
|
-
await ExpoAiKitModule.unloadModel();
|
|
524
|
+
await wrapNative(() => ExpoAiKitModule.unloadModel());
|
|
320
525
|
}
|
|
321
526
|
//# sourceMappingURL=index.js.map
|
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 from './ExpoAiKitModule';\nimport { Platform } from 'react-native';\nimport {\n LLMMessage,\n LLMSendOptions,\n LLMResponse,\n LLMStreamOptions,\n LLMStreamEvent,\n LLMStreamCallback,\n BuiltInModel,\n DownloadableModel,\n ModelError,\n SetModelOptions,\n} from './types';\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\nlet streamIdCounter = 0;\nfunction generateSessionId(): string {\n return `stream_${Date.now()}_${++streamIdCounter}`;\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 // 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 return ExpoAiKitModule.sendMessage(messages, systemPrompt);\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): { promise: Promise<LLMResponse>; stop: () => void } {\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 const sessionId = generateSessionId();\n let finalText = '';\n let stopped = false;\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 const promise = new Promise<LLMResponse>((resolve, reject) => {\n // Subscribe to stream events\n const subscription = ExpoAiKitModule.addListener(\n 'onStreamToken',\n (event: LLMStreamEvent) => {\n // Only process events for this session\n if (event.sessionId !== sessionId) return;\n\n finalText = event.accumulatedText;\n\n // Call the user's callback\n onToken(event);\n\n // If done, clean up and resolve\n if (event.isDone) {\n subscription.remove();\n resolve({ text: finalText });\n }\n }\n );\n\n // Start streaming on native side\n ExpoAiKitModule.startStreaming(messages, systemPrompt, sessionId).catch(\n (error) => {\n subscription.remove();\n reject(error);\n }\n );\n });\n\n const stop = () => {\n if (stopped) return;\n stopped = true;\n ExpoAiKitModule.stopStreaming(sessionId).catch(() => {\n // Ignore errors when stopping\n });\n };\n\n return { promise, stop };\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 platformModels.map((entry) => {\n const status = 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 * 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 ExpoAiKitModule.downloadModel(\n modelId,\n entry.downloadUrl,\n entry.sha256\n );\n } finally {\n subscription?.remove();\n }\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 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 await ExpoAiKitModule.setModel(modelId, minRamBytes, backend);\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 ExpoAiKitModule.unloadModel();\n}\n\n"]}
|
|
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} from './types';\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\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// 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"]}
|
package/build/models.js
CHANGED
|
@@ -12,13 +12,13 @@ export const MODEL_REGISTRY = [
|
|
|
12
12
|
parameterCount: '2.3B',
|
|
13
13
|
quantization: 'mixed-2/4/8-bit',
|
|
14
14
|
downloadUrl: 'https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it.litertlm',
|
|
15
|
-
sha256: '',
|
|
16
|
-
sizeBytes:
|
|
15
|
+
sha256: '181938105e0eefd105961417e8da75903eacda102c4fce9ce90f50b97139a63c',
|
|
16
|
+
sizeBytes: 2_588_147_712, // 2.59GB (exact, HF LFS)
|
|
17
17
|
// Conservative limit for 4GB RAM devices.
|
|
18
18
|
// TODO: Benchmark during Phase 2 testing.
|
|
19
19
|
contextWindow: 8_000,
|
|
20
20
|
minRamBytes: 2_000_000_000, // 2GB — LiteRT-LM memory-maps weights, actual RSS ~1.5GB
|
|
21
|
-
supportedPlatforms: ['android'],
|
|
21
|
+
supportedPlatforms: ['ios', 'android'],
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
id: 'gemma-e4b',
|
|
@@ -26,11 +26,11 @@ export const MODEL_REGISTRY = [
|
|
|
26
26
|
parameterCount: '4.5B',
|
|
27
27
|
quantization: 'mixed-4/8-bit',
|
|
28
28
|
downloadUrl: 'https://huggingface.co/litert-community/gemma-4-E4B-it-litert-lm/resolve/main/gemma-4-E4B-it.litertlm',
|
|
29
|
-
sha256: '',
|
|
30
|
-
sizeBytes:
|
|
29
|
+
sha256: '0b2a8980ce155fd97673d8e820b4d29d9c7d99b8fa6806f425d969b145bd52e0',
|
|
30
|
+
sizeBytes: 3_659_530_240, // 3.66GB (exact, HF LFS)
|
|
31
31
|
contextWindow: 16_000,
|
|
32
32
|
minRamBytes: 3_000_000_000, // 3GB — LiteRT-LM memory-maps weights
|
|
33
|
-
supportedPlatforms: ['android'],
|
|
33
|
+
supportedPlatforms: ['ios', 'android'],
|
|
34
34
|
},
|
|
35
35
|
];
|
|
36
36
|
/**
|
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,
|
|
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"]}
|
package/build/types.d.ts
CHANGED
|
@@ -6,12 +6,53 @@
|
|
|
6
6
|
* - 'cpu': Force CPU — slower (~2-5 tok/s) but works on low-RAM devices
|
|
7
7
|
*/
|
|
8
8
|
export type InferenceBackend = 'auto' | 'gpu' | 'cpu';
|
|
9
|
+
/**
|
|
10
|
+
* Sampling / generation parameters applied to a model session.
|
|
11
|
+
*
|
|
12
|
+
* Support is per-backend (on-device runtimes expose different knobs), so these
|
|
13
|
+
* are best-effort — unsupported fields are ignored rather than erroring:
|
|
14
|
+
*
|
|
15
|
+
* | field | Gemma (LiteRT-LM) | Apple Foundation Models | ML Kit |
|
|
16
|
+
* |-------------|:-----------------:|:-----------------------:|:------:|
|
|
17
|
+
* | temperature | ✓ | ✓ | — |
|
|
18
|
+
* | topK | ✓ | — | — |
|
|
19
|
+
* | topP | ✓ | — | — |
|
|
20
|
+
* | seed | ✓ (iOS only) | — | — |
|
|
21
|
+
* | maxTokens | — | ✓ (max output) | — |
|
|
22
|
+
*
|
|
23
|
+
* Notes:
|
|
24
|
+
* - Gemma/LiteRT-LM has no per-generation output-token cap (its `maxNumTokens`
|
|
25
|
+
* is the total KV-cache size, set at load), so `maxTokens` is not honored
|
|
26
|
+
* there. Its sampler (topK/topP/temperature[/seed]) is fixed at conversation
|
|
27
|
+
* creation, which is why generation config lives on setModel() rather than
|
|
28
|
+
* per-call. `seed` is currently wired on iOS only.
|
|
29
|
+
* - The ML Kit built-in (Android default) does not yet apply generation config;
|
|
30
|
+
* it uses its own defaults.
|
|
31
|
+
*/
|
|
32
|
+
export type GenerationConfig = {
|
|
33
|
+
/** Sampling temperature. Lower = more deterministic. Typically 0.0–2.0. */
|
|
34
|
+
temperature?: number;
|
|
35
|
+
/** Nucleus sampling: number of top logits to consider. Must be > 0. */
|
|
36
|
+
topK?: number;
|
|
37
|
+
/** Nucleus sampling: cumulative probability threshold in [0, 1]. */
|
|
38
|
+
topP?: number;
|
|
39
|
+
/** RNG seed for reproducible sampling (Gemma only). */
|
|
40
|
+
seed?: number;
|
|
41
|
+
/** Maximum number of output tokens to generate (Apple FM / ML Kit only). */
|
|
42
|
+
maxTokens?: number;
|
|
43
|
+
};
|
|
9
44
|
/**
|
|
10
45
|
* Options for setModel.
|
|
11
46
|
*/
|
|
12
47
|
export type SetModelOptions = {
|
|
13
48
|
/** Hardware backend to use for inference. Defaults to 'auto'. */
|
|
14
49
|
backend?: InferenceBackend;
|
|
50
|
+
/**
|
|
51
|
+
* Default sampling parameters for this model session. Applied when the model
|
|
52
|
+
* is activated and used for all subsequent sendMessage/streamMessage calls
|
|
53
|
+
* until the next setModel(). See {@link GenerationConfig} for per-backend support.
|
|
54
|
+
*/
|
|
55
|
+
generation?: GenerationConfig;
|
|
15
56
|
};
|
|
16
57
|
/**
|
|
17
58
|
* Role in a conversation message.
|
|
@@ -33,6 +74,17 @@ export type LLMSendOptions = {
|
|
|
33
74
|
* If a system message exists in the array, this is ignored.
|
|
34
75
|
*/
|
|
35
76
|
systemPrompt?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Abort the request. When the signal fires, the returned promise rejects with
|
|
79
|
+
* an INFERENCE_CANCELLED {@link ModelError}.
|
|
80
|
+
*
|
|
81
|
+
* Note: on-device, non-streaming generation cannot always be interrupted
|
|
82
|
+
* mid-decode — abort always unblocks the caller, but the model may keep
|
|
83
|
+
* computing in the background until it finishes, during which a new
|
|
84
|
+
* sendMessage/streamMessage will throw INFERENCE_BUSY. To truly interrupt a
|
|
85
|
+
* long generation, prefer streamMessage().stop().
|
|
86
|
+
*/
|
|
87
|
+
signal?: AbortSignal;
|
|
36
88
|
};
|
|
37
89
|
/**
|
|
38
90
|
* Response from sendMessage.
|
|
@@ -51,6 +103,15 @@ export type LLMStreamOptions = {
|
|
|
51
103
|
*/
|
|
52
104
|
systemPrompt?: string;
|
|
53
105
|
};
|
|
106
|
+
/**
|
|
107
|
+
* Handle returned by streamMessage.
|
|
108
|
+
*/
|
|
109
|
+
export type LLMStreamHandle = {
|
|
110
|
+
/** Resolves with the final text when streaming completes or is stopped. */
|
|
111
|
+
promise: Promise<LLMResponse>;
|
|
112
|
+
/** Stop streaming. Resolves `promise` with the text accumulated so far. */
|
|
113
|
+
stop: () => void;
|
|
114
|
+
};
|
|
54
115
|
/**
|
|
55
116
|
* Event payload for streaming tokens.
|
|
56
117
|
*/
|
|
@@ -111,14 +172,17 @@ export type DownloadableModel = {
|
|
|
111
172
|
*
|
|
112
173
|
* - 'not-downloaded': Model file is not on disk
|
|
113
174
|
* - 'downloading': Model file is being downloaded
|
|
175
|
+
* - 'downloaded': Model file is on disk but not loaded into memory. Call
|
|
176
|
+
* setModel() to load it. This survives app restarts, so use it to decide
|
|
177
|
+
* whether a (re-)download is needed.
|
|
114
178
|
* - 'loading': File is on disk, model is being loaded into memory for inference
|
|
115
179
|
* - 'ready': Model is loaded in memory and ready for inference
|
|
116
180
|
*/
|
|
117
|
-
export type DownloadableModelStatus = 'not-downloaded' | 'downloading' | 'loading' | 'ready';
|
|
181
|
+
export type DownloadableModelStatus = 'not-downloaded' | 'downloading' | 'downloaded' | 'loading' | 'ready';
|
|
118
182
|
/**
|
|
119
183
|
* Error codes for model-related operations.
|
|
120
184
|
*/
|
|
121
|
-
export type ModelErrorCode = 'MODEL_NOT_FOUND' | 'MODEL_NOT_DOWNLOADED' | 'DOWNLOAD_FAILED' | 'DOWNLOAD_CORRUPT' | 'DOWNLOAD_STORAGE_FULL' | 'INFERENCE_OOM' | 'INFERENCE_FAILED' | 'MODEL_LOAD_FAILED' | 'DEVICE_NOT_SUPPORTED';
|
|
185
|
+
export type ModelErrorCode = 'MODEL_NOT_FOUND' | 'MODEL_NOT_DOWNLOADED' | 'DOWNLOAD_FAILED' | 'DOWNLOAD_CORRUPT' | 'DOWNLOAD_STORAGE_FULL' | 'DOWNLOAD_CANCELLED' | 'INFERENCE_OOM' | 'INFERENCE_FAILED' | 'INFERENCE_BUSY' | 'INFERENCE_CANCELLED' | 'MODEL_LOAD_FAILED' | 'DEVICE_NOT_SUPPORTED' | 'UNKNOWN';
|
|
122
186
|
/**
|
|
123
187
|
* Structured error for model operations.
|
|
124
188
|
*/
|