You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- ios/ExpoLitertLmModule.swift: accept POSIX paths and file:// URLs in loadModel;
retain security-scoped resource access for the lifetime of the loaded model
- example/App.tsx: pass raw DocumentPicker URI through (no caller-side strip);
record cancelLatencyMs even on early cancel before first token
- package.json: 0.1.0 -> 0.2.0-rc.0; npm files now includes
ios/BinaryPods/Frameworks/CLiteRTLM.xcframework +
GemmaModelConstraintProvider.xcframework + rewrap-manifest.json
- ios/BinaryPods/Frameworks/.npmignore: keep xcframeworks in tarball even though
they are gitignored (consumers must not rebuild from source)
- README/CHANGELOG/example docs: SE 3rd gen + Gemma 3 1B INT4 iOS framing,
no Gemma 4 E2B iOS claim; npm badges + View-on-npm link added
Verification (all green):
- npm run build, npx tsc -p example/tsconfig.json --noEmit
- make verify (Layer A/B/C CocoaPods checks)
- npm publish --tag next --dry-run: 48 files, 14.5 MB packed, xcframeworks included
- xcodebuild CODE_SIGNING_ALLOWED=NO -> BUILD SUCCEEDED
Android guardrail: zero diff under android/, src/, lib/.
Pre-release under --tag next; latest stays at 0.1.2 until competition fully ends.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+8-5Lines changed: 8 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,19 +1,24 @@
1
1
# Changelog
2
2
3
-
## 0.2.0-dev.1 — Phase 14 (in progress, 2026-05-15)
3
+
## 0.2.0 — 2026-05-16
4
4
5
+
-**Added:** iOS LiteRT-LM text generation via vendored LiteRTLM-Swift / `CLiteRTLM.xcframework`. Validated on iPhone SE 3rd gen with `gemma3-1b-it-int4.litertlm` (Gemma 3 1B INT4). Gemma 4 E2B remains Android-validated only; it was not tested on SE 3rd gen because of memory limits.
5
6
-**Added:**`sampleMemoryAsync()` — returns the current process `phys_footprint` in MB for Stage B integration measurement (Phase 14 D-07). iOS only; surfaced via colocated `ios/MemoryProbe.swift` (task_info / TASK_VM_INFO). Stage B example app under `example/` exercises the full Expo + RN + native bridge end-to-end against the rewrapped LiteRTLM-Swift xcframework on SE 3rd gen.
6
7
-**Changed:**`scripts/sync-litertlm-swift.sh` rewritten — now fetches rewrapped LiteRTLM-Swift from `helenkwok/LiteRTLM-Swift`'s GitHub Release by tag, verifies SHA-256 against `rewrap-manifest.json`, places artifacts under `ios/BinaryPods/Frameworks/`. Manual sync per Phase 14 D-34. Run `make sync TAG=v<upstream>+rewrap.<n>`.
-**Added:**`ExpoLitertLm.podspec` now reads `vendored_frameworks` and `s.version` from `ios/BinaryPods/Frameworks/rewrap-manifest.json` via Ruby `JSON.parse` at install time. Phase 14 D-31 single source of truth.
9
10
-**Added:** Layer A/B/C verification gates (`scripts/verify-consumption.sh`, `make verify`). Layer A = pod install from fresh consumer; Layer B = grep gate for raw xcframework refs; Layer C = manifest-driven podspec consistency.
10
11
-**Added:**`.github/workflows/verify-consumption.yml` — CI mirrors fork's three-layer gate on macos-latest.
12
+
-**Added:** Expo config plugin (`app.plugin.js`) that injects the split binary pods required for iOS consumers. Verified under `useFrameworks: "static"`; `useFrameworks: "dynamic"` was rejected by CocoaPods because Expo SDK 55 includes static transitive binaries.
13
+
-**Fixed:** iOS `loadModelAsync` accepts either a POSIX path or a `file://` URL, including values returned by `expo-document-picker`.
14
+
-**Fixed:** Stage B example now records `cancelLatencyMs` when cancellation ends before a final token event arrives.
11
15
12
16
-**BREAKING:** iOS path migrated from `MediaPipeTasksGenAI` default-dep to vendored `LiteRTLM-Swift` (`CLiteRTLM.xcframework` + `GemmaModelConstraintProvider.xcframework`). Consumers must run `pod install` after upgrade.
13
17
-**BREAKING:**`ExpoLitertLm.podspec` no longer carries `static_framework = true` — see CocoaPods issue [#11948](https://github.com/CocoaPods/CocoaPods/issues/11948) + offlineaid Phase 14 CONTEXT D-21. Static linkage of a dylib-containing xcframework crashes the host app at launch.
14
18
-**BREAKING:** MediaPipe `.task` support moves to opt-in `MediaPipeFallback` subspec; default install does not include `MediaPipeTasksGenAI` on iOS. Consumers who need it: `pod 'ExpoLitertLm', :subspecs => ['Core', 'MediaPipeFallback']` in their Podfile.
15
-
- Added: `scripts/sync-litertlm-swift.sh` for manual fork sync. `helenkwok/LiteRTLM-Swift` is the upstream fork pinned by SHA; vendoring happens at sync-time, not at `pod install`-time.
16
-
- Added: `scripts/rewrap-xcframework.sh` in the fork resolves upstream issue [#6](https://github.com/mylovelycodes/LiteRTLM-Swift/issues/6) offline (Info.plist `CFBundleShortVersionString`, loose-dylib promotion, dSYM emission). TestFlight validation deferred to v1.2 per offlineaid CONTEXT D-25.
19
+
-**Added:**`scripts/sync-litertlm-swift.sh` for manual fork sync. `helenkwok/LiteRTLM-Swift` is the upstream fork pinned by SHA; vendoring happens at sync-time, not at `pod install`-time.
20
+
-**Added:**`scripts/rewrap-xcframework.sh` in the fork resolves upstream issue [#6](https://github.com/mylovelycodes/LiteRTLM-Swift/issues/6) offline (Info.plist `CFBundleShortVersionString`, loose-dylib promotion, dSYM emission). TestFlight validation deferred to v1.2 per offlineaid CONTEXT D-25.
21
+
-**Synced:** Rewrapped LiteRTLM-Swift `v0.10.2+rewrap.5` (2 xcframeworks, first sha256 `ba2d0c9a6b2a...`).
17
22
18
23
## 0.1.2 — 2026-05-12
19
24
@@ -26,5 +31,3 @@
26
31
## 0.1.0
27
32
28
33
- feat: initial expo-litert-lm — Expo Modules bindings for LiteRT-LM.
**Status:** Android-first. iOS scaffold exists; full LiteRT-LM iOS support pending. Production-tested in [OfflineAid](https://github.com/helenkwok/offlineaid) (audio + chat).
7
+
> Expo Modules bindings for [Google AI Edge LiteRT-LM](https://github.com/google-ai-edge/litert-lm) - on-device Gemma inference for Expo apps. **[View on npm →](https://www.npmjs.com/package/expo-litert-lm)**
8
+
9
+
**Status:** Android production-tested in [OfflineAid](https://github.com/helenkwok/offlineaid) with Gemma 4 E2B chat + audio. iOS LiteRT-LM text generation is validated on iPhone SE 3rd gen with `gemma3-1b-it-int4.litertlm` (Gemma 3 1B INT4); iOS audio/vision remain planned.
6
10
7
11
## What this is
8
12
9
-
Native bridge for Expo SDK 56+ apps to load and run `.litertlm` and `.task` models from Google's `litert-community` Hugging Face org via the official `litertlm-android:0.11.0` SDK. MIT licensed.
13
+
Native bridge for Expo SDK 55+ apps to load and run `.litertlm` and `.task` models from Google's `litert-community` Hugging Face org.
14
+
15
+
- Android uses Google's official `litertlm-android:0.11.0` SDK plus MediaPipe Tasks GenAI.
16
+
- iOS uses vendored LiteRTLM-Swift wrapping the LiteRT-LM C API (`CLiteRTLM.xcframework` + `GemmaModelConstraintProvider.xcframework`, rewrapped from LiteRTLM-Swift `v0.10.2+rewrap.5`).
17
+
- iOS `.task` support is available only through the opt-in `MediaPipeFallback` CocoaPods subspec; the default iOS install is LiteRT-LM only.
18
+
19
+
MIT licensed.
10
20
11
21
## What this is *not*
12
22
@@ -15,10 +25,10 @@ Native bridge for Expo SDK 56+ apps to load and run `.litertlm` and `.task` mode
| iOS LiteRT-LM |Text/chat validated on iPhone SE 3rd gen with Gemma 3 1B INT4; audio/vision deferred| Full |
19
29
| Android API | 31+ (S) | 26+ |
20
30
| MTP / speculative decoding |`ExperimentalFlags.enableSpeculativeDecoding` wired against v0.11.0; blocked upstream on non-Adreno Android by [LiteRT-LM#2211](https://github.com/google-ai-edge/LiteRT-LM/issues/2211) (sampler `dlopen` fails on Tensor G2/G6 — cross-validated 2026-05-12) and [#2227](https://github.com/google-ai-edge/LiteRT-LM/issues/2227) (drafter regression on PowerVR even with sampler loaded). Architecture verified 2.01× lossless on Mac via mlx-vlm. | Not exposed |
21
-
| Audio encoder |Production use | iOS limited |
31
+
| Audio encoder |Android production use; iOS throws a clear not-yet-available error| iOS limited |
Both packages exist deliberately. Pick what fits your app.
@@ -37,7 +47,7 @@ Or pin a specific version in `package.json`:
37
47
38
48
```jsonc
39
49
"dependencies": {
40
-
"expo-litert-lm":"^0.1.1"
50
+
"expo-litert-lm":"^0.2.0"
41
51
}
42
52
```
43
53
@@ -82,10 +92,30 @@ if (await isLiteRtAvailable()) {
82
92
83
93
## Requirements
84
94
85
-
- Expo SDK 56+
95
+
- Expo SDK 55+
86
96
- Android 12 (API 31) or newer
87
-
- A device with GPU support for best performance (e.g. Pixel 7+)
88
-
- Models: `.litertlm` (LiteRT-LM) or `.task` (MediaPipe Tasks GenAI)
97
+
- iOS 17.0 or newer for the LiteRT-LM path
98
+
- Android: a device with GPU support for best performance (e.g. Pixel 7+)
99
+
- iOS: iPhone SE 3rd gen / A15 is the validated floor for Gemma 3 1B INT4. Gemma 4 E2B has not been validated on SE 3rd gen due to memory limits.
100
+
- Models: `.litertlm` (LiteRT-LM) or `.task` (MediaPipe Tasks GenAI; Android by default, iOS only with `MediaPipeFallback`)
101
+
102
+
## iOS Notes
103
+
104
+
`v0.2.0` adds the iOS LiteRT-LM path and ships the rewrapped xcframeworks inside the npm tarball. The included Expo config plugin injects the two binary pods that CocoaPods needs:
105
+
106
+
```jsonc
107
+
{
108
+
"expo": {
109
+
"plugins": ["expo-litert-lm"]
110
+
}
111
+
}
112
+
```
113
+
114
+
The plugin is compatible with Expo's `useFrameworks: "static"` consumer apps. A `useFrameworks: "dynamic"` diagnostic was run on the example app and CocoaPods rejected it because Expo SDK 55 has static transitive binaries (`ExpoModulesCore` / `ExpoModulesJSI`), so the split-binary pod path is intentional.
115
+
116
+
The iOS loader accepts both POSIX paths and `file://` URLs, including values returned by `expo-document-picker`.
117
+
118
+
The SE 3rd gen validation used the default CPU backend. iOS Metal/GPU acceleration is not claimed in this release.
// LiteRTLM-Swift path — default for `.litertlm` models.
89
95
// textOnly: true is REQUIRED for the v1.1 floor-device model
90
96
// (gemma3-1b-it-int4.litertlm has no vision/audio encoders). Stage A
@@ -93,13 +99,16 @@ public final class ExpoLitertLmModule: Module {
93
99
// Expo Modules consumer. Multimodal Gemma 4 callers must set this
94
100
// to false explicitly when that path is wired (Phase 16).
95
101
letengine=LiteRTLMEngine(
96
-
modelPath:URL(fileURLWithPath: modelPath),
102
+
modelPath:modelURL,
97
103
backend: resolvedBackend,
98
104
textOnly:true
99
105
)
100
106
do{
101
107
tryawait engine.load()
102
108
}catch{
109
+
if shouldStopSecurityScopeOnExit {
110
+
modelURL.stopAccessingSecurityScopedResource()
111
+
}
103
112
throwLiteRtModuleError(
104
113
message:self.message(from: error, fallback:"LiteRT model loading failed.")
105
114
)
@@ -114,32 +123,42 @@ public final class ExpoLitertLmModule: Module {
114
123
letengine=MediaPipeFallbackEngine()
115
124
do{
116
125
try engine.load(
117
-
modelPath:modelPath,
126
+
modelPath:resolvedModelPath,
118
127
maxTokens: maxTokens,
119
128
topK: topK,
120
129
temperature: temperature,
121
130
preferredBackend: resolvedBackend
122
131
)
123
132
}catch{
133
+
if shouldStopSecurityScopeOnExit {
134
+
modelURL.stopAccessingSecurityScopedResource()
135
+
}
124
136
throwLiteRtModuleError(
125
137
message:self.message(from: error, fallback:"MediaPipe model loading failed.")
126
138
)
127
139
}
128
140
self.mediaPipeFallback = engine
129
141
self.loadedRuntime =.mediaPipe
130
142
#else
143
+
if shouldStopSecurityScopeOnExit {
144
+
modelURL.stopAccessingSecurityScopedResource()
145
+
}
131
146
throwLiteRtModuleError(
132
147
message:".task models require the MediaPipeFallback subspec. Add `pod 'ExpoLitertLm', :subspecs => ['Core', 'MediaPipeFallback']` to your Podfile per ExpoLitertLm CHANGELOG v0.2.0."
133
148
)
134
149
#endif
135
150
}
136
151
137
152
self.loadedConfig = nextConfig
138
-
self.loadedModelPath = modelPath
153
+
self.loadedModelPath = resolvedModelPath
154
+
if didStartSecurityScope {
155
+
self.securityScopedModelURL = modelURL
156
+
shouldStopSecurityScopeOnExit =false
157
+
}
139
158
140
159
return[
141
160
"backend": resolvedBackend,
142
-
"modelPath":modelPath,
161
+
"modelPath":resolvedModelPath,
143
162
]
144
163
}
145
164
@@ -227,6 +246,25 @@ public final class ExpoLitertLmModule: Module {
0 commit comments