Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/react-native-executorch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
"@huggingface/jinja": "^0.5.0",
"jsonrepair": "^3.12.0",
"jsonschema": "^1.5.0",
"react-native-device-info": "^15.0.2",
"zod": "^4.3.6"
}
}
87 changes: 71 additions & 16 deletions packages/react-native-executorch/src/constants/modelRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Platform } from 'react-native';
import { isEmulatorSync } from 'react-native-device-info';
import * as M from './modelUrls';
import * as OCR from './ocr/models';
import { symbols } from './ocr/symbols';
Expand Down Expand Up @@ -78,19 +79,32 @@ type ConfigOf<V> = Extract<
>;
type BackendsOf<V> = Extract<keyof V, Backend>;

const BACKEND_ORDER: Backend[] = ['xnnpack', 'coreml', 'mlx', 'vulkan', 'qnn'];
const PLATFORM_PREFERENCE: Partial<Record<typeof Platform.OS, Backend[]>> = {
ios: ['coreml', 'mlx', 'xnnpack'],
android: ['vulkan', 'qnn', 'xnnpack'],
};

function firstBackend(variants: AnyVariantMap): Backend {
for (const b of BACKEND_ORDER) {
if (variants[b]) return b;
function applySimulatorPolicy(
backend: Backend,
variants: AnyVariantMap,
explicit: boolean
): Backend {
if (backend !== 'mlx' || Platform.OS !== 'ios' || !isEmulatorSync()) {
return backend;
}
if (!explicit && variants.xnnpack) return 'xnnpack';
throw new RnExecutorchError(
RnExecutorchErrorCode.Internal,
'Model variant map is empty.'
RnExecutorchErrorCode.InvalidConfig,
'The MLX backend requires a physical iOS device and cannot run on the ' +
'simulator.' +
(variants.xnnpack
? " Pass `{ backend: 'xnnpack' }` (or omit `backend`) to run on the " +
'simulator.'
: ' This model ships no simulator-compatible backend.')
);
}

function resolveBackend(
function selectBackend(
variants: AnyVariantMap,
platformDefaults: PlatformDefaults<Backend> | undefined,
requested: Backend | undefined
Expand All @@ -99,17 +113,32 @@ function resolveBackend(
if (platformDefaults) {
if (Platform.OS === 'ios' && platformDefaults.ios)
return platformDefaults.ios;
if (Platform.OS === 'android' && platformDefaults.android) {
if (Platform.OS === 'android' && platformDefaults.android)
return platformDefaults.android;
}
if (platformDefaults.default) return platformDefaults.default;
}
// Implicit platform default: prefer CoreML on iOS, XNNPACK on Android
// whenever the model ships that backend. Models can override via
// `platformDefaults`.
if (Platform.OS === 'ios' && variants.coreml) return 'coreml';
if (Platform.OS === 'android' && variants.xnnpack) return 'xnnpack';
return firstBackend(variants);
// Implicit platform default: walk the platform's preference order (iOS:
// coreml → mlx → xnnpack, Android: vulkan → qnn → xnnpack) and take the
// first backend the model ships. Models can override via `platformDefaults`.
const preference = PLATFORM_PREFERENCE[Platform.OS] ?? [];
for (const b of preference) {
if (variants[b]) return b;
}
// No backend the model ships can run on this platform.
throw new RnExecutorchError(
RnExecutorchErrorCode.InvalidConfig,
`This model ships no backend compatible with ${Platform.OS}.`
);
}

function resolveBackend(
variants: AnyVariantMap,
platformDefaults: PlatformDefaults<Backend> | undefined,
requested: Backend | undefined
): Backend {
const explicit = requested !== undefined;
const backend = selectBackend(variants, platformDefaults, requested);
return applySimulatorPolicy(backend, variants, explicit);
}

function resolveCell(cell: BackendCell, quant: boolean): { modelName: string } {
Expand Down Expand Up @@ -208,6 +237,29 @@ const GEMMA4_E2B_VARIANTS = {
},
};

const GEMMA4_E2B_MM_CONFIG = {
modelName: 'gemma4-e2b-multimodal' as const,
tokenizerSource: M.GEMMA4_E2B_TOKENIZER,
tokenizerConfigSource: M.GEMMA4_E2B_TOKENIZER_CONFIG,
capabilities: ['vision', 'audio'] as const,
audioConfig: {
samplesPerBlock: 7680,
tokensPerBlock: 12,
},
};

const GEMMA4_E2B_MM_VARIANTS = {
mlx: {
base: { ...GEMMA4_E2B_MM_CONFIG, modelSource: M.GEMMA4_E2B_MLX_MM },
},
vulkan: {
base: { ...GEMMA4_E2B_MM_CONFIG, modelSource: M.GEMMA4_E2B_VULKAN_MM },
},
xnnpack: {
base: { ...GEMMA4_E2B_MM_CONFIG, modelSource: M.GEMMA4_E2B_XNNPACK_MM },
},
};

const EFFICIENTNET_V2_S_VARIANTS = {
xnnpack: {
base: {
Expand Down Expand Up @@ -531,7 +583,10 @@ export const models = {
// pick a model by capability ("LLM") rather than by modality.
lfm2_5_vl_1_6b: base(M.LFM2_5_VL_1_6B_QUANTIZED),
lfm2_5_vl_450m: base(M.LFM2_5_VL_450M_QUANTIZED),
gemma4_e2b_multimodal: base(M.GEMMA4_E2B_MM),
gemma4_e2b_multimodal: variant(GEMMA4_E2B_MM_VARIANTS, {
ios: 'mlx',
android: 'vulkan',
}),
},
classification: {
efficientnet_v2_s: variant(EFFICIENTNET_V2_S_VARIANTS),
Expand Down
34 changes: 3 additions & 31 deletions packages/react-native-executorch/src/constants/modelUrls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,37 +133,9 @@ export const GEMMA4_E2B_VULKAN_MODEL = `${GEMMA4_E2B_PREFIX}/vulkan/gemma_4_e2b_
export const GEMMA4_E2B_TOKENIZER = `${GEMMA4_E2B_PREFIX}/tokenizer.json`;
export const GEMMA4_E2B_TOKENIZER_CONFIG = `${GEMMA4_E2B_PREFIX}/tokenizer_config.json`;

const GEMMA4_E2B_MODEL =
Platform.OS === `android` ? GEMMA4_E2B_VULKAN_MODEL : GEMMA4_E2B_MLX_MODEL;

const GEMMA4_E2B_MLX_MM = `${URL_PREFIX}-gemma-4-multimodal/${PREVIOUS_VERSION_TAG}/e2b/mlx/gemma4_e2b_mlx_int4.pte`;
const GEMMA4_E2B_VULKAN_MM = `${URL_PREFIX}-gemma-4-multimodal/${PREVIOUS_VERSION_TAG}/e2b/vulkan/gemma_4_e2b_vulkan_8da4w.pte`;

/**
* @category Models - LLM
*/
export const GEMMA4_E2B = {
modelName: 'gemma4-e2b',
modelSource: GEMMA4_E2B_MODEL,
tokenizerSource: GEMMA4_E2B_TOKENIZER,
tokenizerConfigSource: GEMMA4_E2B_TOKENIZER_CONFIG,
} as const;

/**
* @category Models - LLM Multimodal
*/
export const GEMMA4_E2B_MM = {
modelName: 'gemma4-e2b-multimodal',
modelSource:
Platform.OS === `android` ? GEMMA4_E2B_VULKAN_MM : GEMMA4_E2B_MLX_MM,
tokenizerSource: GEMMA4_E2B_TOKENIZER,
tokenizerConfigSource: GEMMA4_E2B_TOKENIZER_CONFIG,
capabilities: ['vision', 'audio'],
audioConfig: {
samplesPerBlock: 7680,
tokensPerBlock: 12,
},
} as const;
export const GEMMA4_E2B_MLX_MM = `${URL_PREFIX}-gemma-4-multimodal/${PREVIOUS_VERSION_TAG}/e2b/mlx/gemma4_e2b_mlx_int4.pte`;
export const GEMMA4_E2B_VULKAN_MM = `${URL_PREFIX}-gemma-4-multimodal/${PREVIOUS_VERSION_TAG}/e2b/vulkan/gemma_4_e2b_vulkan_8da4w.pte`;
export const GEMMA4_E2B_XNNPACK_MM = `${URL_PREFIX}-gemma-4-multimodal/${PREVIOUS_VERSION_TAG}/e2b/xnnpack/gemma_4_e2b_xnnpack_8da4w.pte`;

/**
* @category Models - LLM
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<key>BinaryPath</key>
<string>ExecutorchLib.framework/ExecutorchLib</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<string>ios-arm64-simulator</string>
<key>LibraryPath</key>
<string>ExecutorchLib.framework</string>
<key>SupportedArchitectures</key>
Expand All @@ -17,12 +17,14 @@
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>ExecutorchLib.framework/ExecutorchLib</string>
<key>LibraryIdentifier</key>
<string>ios-arm64-simulator</string>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>ExecutorchLib.framework</string>
<key>SupportedArchitectures</key>
Expand All @@ -31,8 +33,6 @@
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
Expand Down
Binary file not shown.
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15066,6 +15066,7 @@ __metadata:
react: "npm:19.2.3"
react-native: "npm:0.85.3"
react-native-builder-bob: "npm:^0.40.12"
react-native-device-info: "npm:^15.0.2"
typescript: "npm:~5.9.2"
zod: "npm:^4.3.6"
peerDependencies:
Expand Down
Loading