Skip to content

Commit 0298d63

Browse files
committed
use a third-party huggingface/transformers library and run expo prebuild in example
1 parent 21468e6 commit 0298d63

File tree

82 files changed

+5457
-231
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+5457
-231
lines changed

README.md

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Link the `onnxruntime-react-native` library:
4848
```sh
4949
npx react-native link onnxruntime-react-native
5050
```
51+
5152
</details>
5253

5354
<details>
@@ -56,14 +57,9 @@ npx react-native link onnxruntime-react-native
5657
Add the Expo plugin configuration in `app.json` or `app.config.js`:
5758

5859
```json
59-
{
60-
"expo": {
61-
"plugins": [
62-
"onnxruntime-react-native"
63-
]
64-
}
65-
}
60+
{ "expo": { "plugins": ["onnxruntime-react-native"] } }
6661
```
62+
6763
</details>
6864

6965
### 4. Babel Configuration
@@ -76,8 +72,8 @@ module.exports = {
7672
// ... your existing config
7773
plugins: [
7874
// ... your existing plugins
79-
"babel-plugin-transform-import-meta"
80-
]
75+
'babel-plugin-transform-import-meta',
76+
],
8177
};
8278
```
8379

@@ -92,18 +88,17 @@ You can set up a development client using one of these methods:
9288
- **[EAS Development Build](https://docs.expo.dev/develop/development-builds/introduction/)**: Create a custom development client using EAS Build
9389
- **[Expo Prebuild](https://docs.expo.dev/workflow/prebuild/)**: Eject to a bare workflow to access native code
9490

95-
9691
## Usage
9792

9893
### Text Generation
9994

10095
```javascript
101-
import React, { useState, useEffect } from "react";
102-
import { View, Text, Button } from "react-native";
103-
import { Pipeline } from "react-native-transformers";
96+
import React, { useState, useEffect } from 'react';
97+
import { View, Text, Button } from 'react-native';
98+
import { Pipeline } from 'react-native-transformers';
10499

105100
export default function App() {
106-
const [output, setOutput] = useState("");
101+
const [output, setOutput] = useState('');
107102
const [isLoading, setIsLoading] = useState(false);
108103
const [isModelReady, setIsModelReady] = useState(false);
109104

@@ -117,44 +112,44 @@ export default function App() {
117112
try {
118113
// Load a small Llama model
119114
await Pipeline.TextGeneration.init(
120-
"Felladrin/onnx-Llama-160M-Chat-v1",
121-
"onnx/decoder_model_merged.onnx",
115+
'Felladrin/onnx-Llama-160M-Chat-v1',
116+
'onnx/decoder_model_merged.onnx',
122117
{
123118
// The fetch function is required to download model files
124119
fetch: async (url) => {
125120
// In a real app, you might want to cache the downloaded files
126121
const response = await fetch(url);
127122
return response.url;
128-
}
123+
},
129124
}
130125
);
131126
setIsModelReady(true);
132127
} catch (error) {
133-
console.error("Error loading model:", error);
134-
alert("Failed to load model: " + error.message);
128+
console.error('Error loading model:', error);
129+
alert('Failed to load model: ' + error.message);
135130
} finally {
136131
setIsLoading(false);
137132
}
138133
};
139134

140135
const generateText = () => {
141-
setOutput("");
136+
setOutput('');
142137
// Generate text from the prompt and update the UI as tokens are generated
143138
Pipeline.TextGeneration.generate(
144-
"Write a short poem about programming:",
139+
'Write a short poem about programming:',
145140
(text) => setOutput(text)
146141
);
147142
};
148143

149144
return (
150145
<View style={{ padding: 20 }}>
151146
<Button
152-
title={isModelReady ? "Generate Text" : "Load Model"}
147+
title={isModelReady ? 'Generate Text' : 'Load Model'}
153148
onPress={isModelReady ? generateText : loadModel}
154149
disabled={isLoading}
155150
/>
156151
<Text style={{ marginTop: 20 }}>
157-
{output || "Generated text will appear here"}
152+
{output || 'Generated text will appear here'}
158153
</Text>
159154
</View>
160155
);
@@ -166,18 +161,18 @@ export default function App() {
166161
For Expo applications, use `expo-file-system` to download models with progress tracking:
167162

168163
```javascript
169-
import * as FileSystem from "expo-file-system";
170-
import { Pipeline } from "react-native-transformers";
164+
import * as FileSystem from 'expo-file-system';
165+
import { Pipeline } from 'react-native-transformers';
171166

172167
// In your model loading function
173-
await Pipeline.TextGeneration.init("model-repo", "model-file", {
168+
await Pipeline.TextGeneration.init('model-repo', 'model-file', {
174169
fetch: async (url) => {
175-
const localPath = FileSystem.cacheDirectory + url.split("/").pop();
170+
const localPath = FileSystem.cacheDirectory + url.split('/').pop();
176171

177172
// Check if file already exists
178173
const fileInfo = await FileSystem.getInfoAsync(localPath);
179174
if (fileInfo.exists) {
180-
console.log("Model already downloaded, using cached version");
175+
console.log('Model already downloaded, using cached version');
181176
return localPath;
182177
}
183178

@@ -187,28 +182,31 @@ await Pipeline.TextGeneration.init("model-repo", "model-file", {
187182
localPath,
188183
{},
189184
(progress) => {
190-
const percentComplete = progress.totalBytesWritten / progress.totalBytesExpectedToWrite;
191-
console.log(`Download progress: ${(percentComplete * 100).toFixed(1)}%`);
185+
const percentComplete =
186+
progress.totalBytesWritten / progress.totalBytesExpectedToWrite;
187+
console.log(
188+
`Download progress: ${(percentComplete * 100).toFixed(1)}%`
189+
);
192190
}
193191
);
194192

195193
const result = await downloadResumable.downloadAsync();
196194
return result?.uri;
197-
}
195+
},
198196
});
199197
```
200198
201199
## Supported Models
202200
203201
`react-native-transformers` works with ONNX-formatted models from Hugging Face. Here are some recommended models based on size and performance:
204202
205-
| Model | Type | Size | Description |
206-
|-------|------|------|-------------|
207-
| [Felladrin/onnx-Llama-160M-Chat-v1](https://huggingface.co/Felladrin/onnx-Llama-160M-Chat-v1) | Text Generation | ~300MB | Small Llama model (160M parameters) |
208-
| [microsoft/Phi-3-mini-4k-instruct-onnx-web](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx-web) | Text Generation | ~1.5GB | Microsoft's Phi-3-mini model |
209-
| [Xenova/distilgpt2_onnx-quantized](https://huggingface.co/Xenova/distilgpt2_onnx-quantized) | Text Generation | ~165MB | Quantized DistilGPT-2 |
210-
| [Xenova/tiny-mamba-onnx](https://huggingface.co/Xenova/tiny-mamba-onnx) | Text Generation | ~85MB | Tiny Mamba model |
211-
| [Xenova/all-MiniLM-L6-v2-onnx](https://huggingface.co/Xenova/all-MiniLM-L6-v2-onnx) | Text Embedding | ~80MB | Sentence embedding model |
203+
| Model | Type | Size | Description |
204+
| ------------------------------------------------------------------------------------------------------------- | --------------- | ------ | ----------------------------------- |
205+
| [Felladrin/onnx-Llama-160M-Chat-v1](https://huggingface.co/Felladrin/onnx-Llama-160M-Chat-v1) | Text Generation | ~300MB | Small Llama model (160M parameters) |
206+
| [microsoft/Phi-3-mini-4k-instruct-onnx-web](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx-web) | Text Generation | ~1.5GB | Microsoft's Phi-3-mini model |
207+
| [Xenova/distilgpt2_onnx-quantized](https://huggingface.co/Xenova/distilgpt2_onnx-quantized) | Text Generation | ~165MB | Quantized DistilGPT-2 |
208+
| [Xenova/tiny-mamba-onnx](https://huggingface.co/Xenova/tiny-mamba-onnx) | Text Generation | ~85MB | Tiny Mamba model |
209+
| [Xenova/all-MiniLM-L6-v2-onnx](https://huggingface.co/Xenova/all-MiniLM-L6-v2-onnx) | Text Embedding | ~80MB | Sentence embedding model |
212210
213211
## API Reference
214212
@@ -229,12 +227,12 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
229227
## Acknowledgements
230228
231229
- [ONNX Runtime](https://onnxruntime.ai/) for efficient model execution on mobile devices
232-
- [@xenova/transformers](https://www.npmjs.com/package/@xenova/transformers) for transformer model implementations
230+
- [@huggingface/transformers](github:mybigday/transformers.js-rn#merge) for transformer model implementations
233231
- [Hugging Face](https://huggingface.co/) for providing pre-trained models and model hosting
234232
235233
## External Links
236234
237235
- [Expo Plugins Documentation](https://docs.expo.dev/guides/config-plugins/)
238236
- [ONNX Runtime Documentation](https://onnxruntime.ai/)
239237
- [Hugging Face Model Hub](https://huggingface.co/models)
240-
- [ONNX Format Documentation](https://onnx.ai/onnx/intro/)
238+
- [ONNX Format Documentation](https://onnx.ai/onnx/intro/)

example/android/.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# OSX
2+
#
3+
.DS_Store
4+
5+
# Android/IntelliJ
6+
#
7+
build/
8+
.idea
9+
.gradle
10+
local.properties
11+
*.iml
12+
*.hprof
13+
.cxx/
14+
15+
# Bundle artifacts
16+
*.jsbundle

example/android/app/build.gradle

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
apply plugin: "com.android.application"
2+
apply plugin: "org.jetbrains.kotlin.android"
3+
apply plugin: "com.facebook.react"
4+
5+
def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
6+
7+
/**
8+
* This is the configuration block to customize your React Native Android app.
9+
* By default you don't need to apply any configuration, just uncomment the lines you need.
10+
*/
11+
react {
12+
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
13+
reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
14+
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
15+
codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
16+
17+
enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean()
18+
// Use Expo CLI to bundle the app, this ensures the Metro config
19+
// works correctly with Expo projects.
20+
cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim())
21+
bundleCommand = "export:embed"
22+
23+
/* Folders */
24+
// The root of your project, i.e. where "package.json" lives. Default is '../..'
25+
// root = file("../../")
26+
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
27+
// reactNativeDir = file("../../node_modules/react-native")
28+
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
29+
// codegenDir = file("../../node_modules/@react-native/codegen")
30+
31+
/* Variants */
32+
// The list of variants to that are debuggable. For those we're going to
33+
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
34+
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
35+
// debuggableVariants = ["liteDebug", "prodDebug"]
36+
37+
/* Bundling */
38+
// A list containing the node command and its flags. Default is just 'node'.
39+
// nodeExecutableAndArgs = ["node"]
40+
41+
//
42+
// The path to the CLI configuration file. Default is empty.
43+
// bundleConfig = file(../rn-cli.config.js)
44+
//
45+
// The name of the generated asset file containing your JS bundle
46+
// bundleAssetName = "MyApplication.android.bundle"
47+
//
48+
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
49+
// entryFile = file("../js/MyApplication.android.js")
50+
//
51+
// A list of extra flags to pass to the 'bundle' commands.
52+
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
53+
// extraPackagerArgs = []
54+
55+
/* Hermes Commands */
56+
// The hermes compiler command to run. By default it is 'hermesc'
57+
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
58+
//
59+
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
60+
// hermesFlags = ["-O", "-output-source-map"]
61+
62+
/* Autolinking */
63+
autolinkLibrariesWithApp()
64+
}
65+
66+
/**
67+
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
68+
*/
69+
def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean()
70+
71+
/**
72+
* The preferred build flavor of JavaScriptCore (JSC)
73+
*
74+
* For example, to use the international variant, you can use:
75+
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
76+
*
77+
* The international variant includes ICU i18n library and necessary data
78+
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
79+
* give correct results when using with locales other than en-US. Note that
80+
* this variant is about 6MiB larger per architecture than default.
81+
*/
82+
def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
83+
84+
android {
85+
ndkVersion rootProject.ext.ndkVersion
86+
87+
buildToolsVersion rootProject.ext.buildToolsVersion
88+
compileSdk rootProject.ext.compileSdkVersion
89+
90+
namespace 'transformers.example'
91+
defaultConfig {
92+
applicationId 'transformers.example'
93+
minSdkVersion rootProject.ext.minSdkVersion
94+
targetSdkVersion rootProject.ext.targetSdkVersion
95+
versionCode 1
96+
versionName "1.0.0"
97+
}
98+
signingConfigs {
99+
debug {
100+
storeFile file('debug.keystore')
101+
storePassword 'android'
102+
keyAlias 'androiddebugkey'
103+
keyPassword 'android'
104+
}
105+
}
106+
buildTypes {
107+
debug {
108+
signingConfig signingConfigs.debug
109+
}
110+
release {
111+
// Caution! In production, you need to generate your own keystore file.
112+
// see https://reactnative.dev/docs/signed-apk-android.
113+
signingConfig signingConfigs.debug
114+
shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
115+
minifyEnabled enableProguardInReleaseBuilds
116+
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
117+
crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true)
118+
}
119+
}
120+
packagingOptions {
121+
jniLibs {
122+
useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false)
123+
}
124+
}
125+
androidResources {
126+
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~'
127+
}
128+
}
129+
130+
// Apply static values from `gradle.properties` to the `android.packagingOptions`
131+
// Accepts values in comma delimited lists, example:
132+
// android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini
133+
["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop ->
134+
// Split option: 'foo,bar' -> ['foo', 'bar']
135+
def options = (findProperty("android.packagingOptions.$prop") ?: "").split(",");
136+
// Trim all elements in place.
137+
for (i in 0..<options.size()) options[i] = options[i].trim();
138+
// `[] - ""` is essentially `[""].filter(Boolean)` removing all empty strings.
139+
options -= ""
140+
141+
if (options.length > 0) {
142+
println "android.packagingOptions.$prop += $options ($options.length)"
143+
// Ex: android.packagingOptions.pickFirsts += '**/SCCS/**'
144+
options.each {
145+
android.packagingOptions[prop] += it
146+
}
147+
}
148+
}
149+
150+
dependencies {
151+
// The version of react-native is set by the React Native Gradle Plugin
152+
implementation("com.facebook.react:react-android")
153+
154+
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
155+
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
156+
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
157+
158+
if (isGifEnabled) {
159+
// For animated gif support
160+
implementation("com.facebook.fresco:animated-gif:${expoLibs.versions.fresco.get()}")
161+
}
162+
163+
if (isWebpEnabled) {
164+
// For webp support
165+
implementation("com.facebook.fresco:webpsupport:${expoLibs.versions.fresco.get()}")
166+
if (isWebpAnimatedEnabled) {
167+
// Animated webp support
168+
implementation("com.facebook.fresco:animated-webp:${expoLibs.versions.fresco.get()}")
169+
}
170+
}
171+
172+
if (hermesEnabled.toBoolean()) {
173+
implementation("com.facebook.react:hermes-android")
174+
} else {
175+
implementation jscFlavor
176+
}
177+
}

example/android/app/debug.keystore

2.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)