Skip to content

feat(constants)!: switch URLs to v0.9.0 layout + add MODEL_REGISTRY#1148

Open
msluszniak wants to merge 14 commits into
mainfrom
@ms/model-registry
Open

feat(constants)!: switch URLs to v0.9.0 layout + add MODEL_REGISTRY#1148
msluszniak wants to merge 14 commits into
mainfrom
@ms/model-registry

Conversation

@msluszniak
Copy link
Copy Markdown
Member

@msluszniak msluszniak commented May 13, 2026

Description

Refreshes every URL constant to the restructured HF layout under
resolve/v0.9.0 and adds the typed models accessor (replaces the
flat MODEL_REGISTRY export from modelUrls.ts).

URL refresh

All URLs follow <model>_<size>_<backend>_<precision>.pte, files sit
under per-size and per-backend directories on HF.

  • modelUrls.ts — every URL rewritten; multi-backend URLs hoisted here so the registry stays declarative. The lfm2_5_350m_xnnpack_8w4da.pte typo is corrected to _8da4w.pte.
  • ocr/models.ts, tts/models.ts, tts/voices.ts — paths updated to the new shape.
  • versions.tsVERSION_TAG → resolve/v0.9.0; PREVIOUS_VERSION_TAG = resolve/v0.8.0 retained for the @deprecated Llama QLoRA aliases.

models accessor

New constants/modelRegistry.ts exports models, a typed accessor
grouped by capability. Following review (Bartek/Kuba):

  • Function-only, lowercase: entries are always called — models.llm.llama3_2_3b() — no dual value/function shape. Group and entry keys are lowercase (matching the underlying .pte filenames).
  • Groups: llm, multimodal (renamed from vlm — Gemma 4 will add audio + vision), classification, object_detection, semantic_segmentation, instance_segmentation, style_transfer, speech_to_text, text_to_speech, text_embedding, image_embedding, image_generation, vad, ocr.
  • Backend selection is type-enforced and wired through at runtime:
models.llm.llama3_2_3b()                       // default (quantized, platform-default backend)
models.llm.llama3_2_3b({ quant: false })       // base
models.text_embedding.distiluse_base_multilingual_cased_v2({ backend: 'coreml' })
  • Defaults to the quantized variant when { quant } is omitted.
  • text_to_speech.kokoro_small/kokoro_medium + voices.* are first-class entries.
  • ocr({ language }) takes a typed language code.

ESLint's camelcase rule is relaxed to properties: 'never' so the
snake_case property keys pass while bindings/functions stay camelCase.

Example apps + docs

  • All example apps migrated to models.*(). Heavily-used groups are destructured at the top of the file (const segmentation = models.semantic_segmentation;) per Kuba's suggestion.
  • Picker entries compared by modelName to handle accessor-function values.
  • bare-rn LLM demo switched to LFM-2.5.
  • Model Registry docs page rewritten for the new accessor; the 0.8.x version's anchor is repointed to its own version to survive the rename.

Deprecations

  • LLAMA3_2_3B_QLORA, LLAMA3_2_1B_QLORA@deprecated; the .pte files stay at v0.8.0 and the constants still resolve those URLs. Use LLAMA3_2_*_SPINQUANT going forward.

Introduces a breaking change?

  • Yes
  • No

URL paths under ${VERSION_TAG} change — code that hardcoded
resolve/v0.8.0 URLs through the constants keeps working only if it
read them at runtime. The flat MODEL_REGISTRY = { ALL_MODELS: {...} }
export from modelUrls.ts is removed; the new models accessor is
the replacement. The internal URL→name lookup (getModelNameForUrl)
is preserved.

Type of change

  • New feature (change which adds functionality)
  • Other (chores, tests, code style improvements etc.)

Tested on

  • iOS
  • Android

yarn typecheck and yarn lint clean across the monorepo. Every
example app runs against the v0.9.0 HF state.

Testing instructions

yarn typecheck
yarn lint

In application code:

import { models } from 'react-native-executorch';

const llm = useLLM({ model: models.llm.llama3_2_3b() });
const emb = useTextEmbeddings({
  model: models.text_embedding.distiluse_base_multilingual_cased_v2({ backend: 'coreml' }),
});

Related issues

#431
#612

Checklist

  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the documentation accordingly
  • My changes generate no new warnings

@msluszniak msluszniak assigned msluszniak and unassigned msluszniak May 13, 2026
@msluszniak msluszniak added feature PRs that implement a new feature labels May 13, 2026
@msluszniak msluszniak marked this pull request as ready for review May 13, 2026 15:16
@barhanc barhanc self-requested a review May 13, 2026 15:22
URL refresh
-----------
Every URL constant in the library now points at the restructured HF
layout under `resolve/v0.9.0`. File names follow
`<model>_<size>_<backend>_<precision>.pte`, files sit under per-size
and per-backend directories. Affects:

- modelUrls.ts: 170 URL refs rewritten to new paths. The 8da4w-typo
  file `lfm2_5_350m_xnnpack_8w4da.pte` is corrected to `..._8da4w.pte`.
- ocr/models.ts: CRAFT detector URL + CRNN per-language URL template
  switch to the new `<lang>/xnnpack/crnn_<lang>_xnnpack_fp32.pte` shape.
- tts/models.ts: Kokoro consts re-rooted to
  `<size>/xnnpack/kokoro_<size>_<component>_xnnpack_fp32.pte`.
- tts/voices.ts: voices/ and phonemizer/ asset paths kept in place;
  only the `${VERSION_TAG}` value bumps.
- versions.ts: VERSION_TAG -> resolve/v0.9.0. NEXT_VERSION_TAG
  collapsed into VERSION_TAG. PREVIOUS_VERSION_TAG=resolve/v0.8.0
  retained for the two @deprecated Llama QLoRA aliases (LLAMA3_2_*_QLORA)
  that continue to resolve their v0.8.0 file. SpinQuant is the canonical
  quantized Llama 3.2 variant going forward.

MODEL_REGISTRY
--------------
Adds `constants/modelRegistry.ts` — a typed accessor grouped by
capability (LLM, VLM, CLASSIFICATION, OBJECT_DETECTION,
SEMANTIC_SEGMENTATION, INSTANCE_SEGMENTATION, STYLE_TRANSFER,
SPEECH_TO_TEXT, TEXT_EMBEDDING, IMAGE_EMBEDDING, IMAGE_GENERATION,
VAD). Each entry is callable with `{ quant, backend }`:

  MODEL_REGISTRY.LLM.LLAMA3_2_3B                  // default (base)
  MODEL_REGISTRY.LLM.LLAMA3_2_3B({ quant: true }) // SpinQuant

When read as a value (object access), returns the default config; when
called, resolves the requested variant. `backend` is accepted in the
signature for forward-compat but the library still picks via
`Platform.OS` at module load.

The previous flat `MODEL_REGISTRY = { ALL_MODELS: {...} }` export in
modelUrls.ts is removed; its internal-only consumer (the urlToModelName
lookup) now reads from a private `_ALL_MODELS` array.

Resolves the JS-API side of the HF naming convention migration.
The umbrella lfm-2.5 HF repo hosts two distinct models — the text LLM
(1.2B + 350M) and the vision-language model (1.6B + 450M). The
migrator collapsed the VL size tokens (`vl_1_6b`, `vl_450m`) to bare
numeric sizes, making VL 1.6B indistinguishable from a hypothetical
text 1.6B variant. It also left the four per-variant tokenizers at
their legacy `lfm2.5-*/` paths instead of moving them next to the new
backend dirs.

HF state (separate commits on the repo):
  - VL .pte files renamed to `vl_<size>/xnnpack/lfm_2_5_vl_<size>_*.pte`
  - tokenizers moved into `<size>/` and `vl_<size>/` next to each cell
  - legacy `lfm2.5-*-instruct/` and `lfm2.5-VL-*/` dirs cleaned out
  - config.json files refreshed (vl_* configs now carry
    `model: lfm_2_5_vl` + `capabilities: [vision, text-generation]`)

This commit refreshes the matching URL constants in modelUrls.ts so
every LFM2.5 model points at its new HF path.
Covers the new grouped MODEL_REGISTRY shape (capability groups with
callable accessors), the `{ quant, backend }` options, default vs
quantized resolution, the still-supported direct-import pattern, and a
short migration note from the previous flat `ALL_MODELS` dict.
22 files updated across apps/llm, apps/computer-vision, apps/speech,
apps/text-embeddings, and apps/bare-rn. Each flat model-constant import
is replaced with the corresponding `MODEL_REGISTRY.<GROUP>.<NAME>` (or
`(...)({ quant: true })` for quantized variants). Llama QLoRA aliases
remain imported under their flat names — they're deprecated and not
part of the registry.

Net effect: -242 / +158 lines (collapsed imports, terser callsites).
Apps now serve as the canonical usage example for the typed registry.
…ctions

useState auto-invokes function-typed initial values as lazy initializers, so
passing a MODEL_REGISTRY accessor unwraps it into a plain config — breaking
reference equality against the accessor stored in MODELS. Compare by modelName
(falling back to === for picker users without one, e.g. VoiceConfig).
Each accessor's `backend` parameter is now typed to exactly the backends the
model ships with — passing an unsupported one is a compile-time error.
`Platform.OS` still picks the default when `backend` is omitted. The per-
backend (quant × backend) variant matrix lives in modelRegistry.ts so
modelUrls.ts stays flat-per-model.

Unifies DISTILUSE_BASE_MULTILINGUAL_CASED_V2 to one accessor with
xnnpack + coreml; the _8DA4W and _COREML named constants stay as
deprecated aliases.
…ariant

Bare accessors (and undefined `quant`) now resolve to the quantized
variant when one is published; pass `{ quant: false }` to opt out. Docs
and example apps are updated to match — dual pickers keep both rows by
making the FP32 entry the explicit opt-out.
@msluszniak msluszniak force-pushed the @ms/model-registry branch from 667d6b3 to fc5eeb0 Compare May 14, 2026 09:36
Comment thread apps/llm/components/ModelPicker.tsx Outdated
Comment on lines +29 to +37
/**
* An accessor that behaves as the platform-default config when read as a value
* (e.g. `MODEL_REGISTRY.LLM.LLAMA3_2_3B.modelName`) and as a function when
* called (e.g. `MODEL_REGISTRY.LLM.LLAMA3_2_3B({ quant: false })`).
*/
type Accessor<
C extends { modelName: string },
B extends Backend = Backend,
> = C & ((opts?: ModelOpts<B>) => C);
Copy link
Copy Markdown
Contributor

@barhanc barhanc May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should make the accessor behave like both a value and a function and just make the user explicitly call e.g. MODEL_REGISTRY.LLM.LLAMA3_2_3B() for default config (perhaps with some stylistic changes like changing the names to lowercase to indicate these are getters and not constants; the use pattern in the user code would be something like const LLAMA3_2_3B = models.llm.llama3_2_3b()). I feel the current approach might generate some problems e.g. when comparing models as in example apps using sameValue workaround.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, currently I'm in favour of making these. The same problem with default config is going to fire on every useMemo([accessor]), every useCallback(fn, [accessor]), every Object.is-based selector in Redux/Zustand. And stylistically, const LLAMA3_2_3B = models.llm.llama3_2_3b()) fits better imho.

Copy link
Copy Markdown
Member Author

@msluszniak msluszniak May 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc: @chmjkb when reviewing, please mind that we must agree on something here. When decided, I can implement specific version.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to allow snake case here?
I can change it to be an option here:

    // `properties: 'never'` lets the lowercase snake_case keys in `models`
    // (e.g. `models.text_to_speech.kokoro_small`, mirroring the underlying
    // `.pte` filenames) pass while still requiring camelCase for variable
    // and function declarations.
    'camelcase': ['error', { properties: 'never' }],

@msluszniak msluszniak requested a review from mkopcins May 14, 2026 11:48
@barhanc
Copy link
Copy Markdown
Contributor

barhanc commented May 14, 2026

There are some problems with HF repos:



That's what I managed to find, probably someone else should also have a look at the updated HF repos. I will be testing example apps now.

Comment thread packages/react-native-executorch/src/constants/modelUrls.ts Outdated
Comment thread packages/react-native-executorch/src/constants/modelUrls.ts Outdated
Comment thread packages/react-native-executorch/src/constants/modelUrls.ts Outdated
Comment thread packages/react-native-executorch/src/constants/modelUrls.ts Outdated
Comment thread packages/react-native-executorch/src/constants/modelUrls.ts Outdated
Each repo's v0.9.0 tag was retagged or rewritten today to match
MODEL_SPEC.md. Sync the constants:

- clip-vit-base-patch32: image/text encoders now live under their
  component-tokenized filenames (the migration silently dropped text
  and pointed both image and text constants at the image encoder).
- deeplab-v3, fcn: separate filenames per backbone (mobilenet_v3_large,
  resnet50, resnet101 for deeplab; resnet50, resnet101 for fcn). All
  three deeplab constants previously resolved to the same blob.
- fast-sam: size-tokened paths (S vs X) for both xnnpack and coreml.
  FASTSAM_S and FASTSAM_X were aliased to the X variant.
- qwen-3.5: tokenizers moved out of the legacy Qwen3.5-{0.8B,2B}/
  directories into the canonical <size>/ layout matching lfm-2.5.
… size-first paths

`MODEL_REGISTRY` accessors default to the quantized variant when one is
published, so the non-quantized slot is no longer the runtime default.
Rename the `BackendCell.default` key to `base` so the field name matches
what it actually represents (the unquantized base variant) and stops
fighting the new runtime default. `PlatformDefaults.default` is unrelated
(platform-fallback backend) and is unchanged.

Also point FastSAM URLs at the size-first HF layout:
  <size>/<backend>/fast_sam_<size>_<backend>_<precision>.pte
matching the yolo26-seg / lfm-2.5 convention.
Copy link
Copy Markdown
Contributor

@barhanc barhanc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding HF repos, the DeepLab and FCN are not categorized by size (the same case as FastSAM before). Other HF repos look fine :)

Comment thread packages/react-native-executorch/src/constants/modelRegistry.ts Outdated
@msluszniak
Copy link
Copy Markdown
Member Author

Regarding HF repos, the DeepLab and FCN are not categorized by size (the same case as FastSAM before). Other HF repos look fine :)

@barhanc I don't think these are sizes per se in this case. You have specified only the backbone of the final model. These models might be smaller or bigger but semantically do not indicate sizes immediately (in comparison where models from the same family have different number of parameters and the name derives from this number of is explicitly named s,m,l, xl etc.).

modelRegistry.ts duplicated the same `${URL_PREFIX}-…/${VERSION_TAG}/…`
strings that modelUrls.ts already had inline in each Platform.OS branch.
Hoist a single set of per-backend URL constants into modelUrls.ts and
have both consumers reference them, so each URL string lives in exactly
one place.

- Add per-backend exports for efficientnet-v2-s, ssdlite320-mobilenet-v3-
  large, rfdetr-nano-detector, rfdetr-nano-segmentation, fast-sam {s,x},
  distiluse-base-multilingual-cased-v2.
- Add `styleTransferUrls(display, slug)` helper for the 4 style-transfer
  styles; the registry's `styleTransferVariants` now consumes it.
- Drop the now-unused `URL_PREFIX, VERSION_TAG` import from
  modelRegistry.ts.

Addresses #1148 (comment)
@msluszniak msluszniak linked an issue May 18, 2026 that may be closed by this pull request
Comment thread apps/bare-rn/App.tsx
Comment thread apps/computer-vision/app/instance_segmentation/index.tsx Outdated
Comment thread apps/llm/components/ModelPicker.tsx Outdated
Comment thread docs/docs/05-utilities/model-registry.md Outdated
Comment thread docs/docs/05-utilities/model-registry.md Outdated
Comment thread docs/docs/05-utilities/model-registry.md Outdated
Comment on lines +39 to +40
base?: { modelName: string };
quant?: { modelName: string };
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if we can make it a string literal union?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but maybe its overengineering, you decide :D

…al/tts/ocr

Adopts Bartek's feedback on #1148 — the accessor is no longer dual-shaped
(value AND function). Each leaf is a pure function: call it (optionally with
\`{ quant, backend }\`) to get the resolved config. This eliminates the
\`useState\` lazy-init footgun and \`useMemo\`/\`useCallback\` dep hazards, so
pickers fall back to plain \`===\` reference equality (drops the \`sameValue\`
workaround across four \`ModelPicker.tsx\` files).

Renames:
- \`MODEL_REGISTRY\` → \`models\` (lowercase top-level)
- group keys lowercased: \`LLM\` → \`llm\`, etc.
- per Kuba: \`vlm\` → \`multimodal\` (anticipates audio-capable LMs like Gemma 4)

Adds:
- \`models.text_to_speech\` group: \`kokoro_small\`, \`kokoro_medium\`, plus
  voices as plain configs under \`voices\` (no quant/backend axis).
- \`models.ocr({ language })\` parameterized accessor — covers all
  ISO language tokens via a runtime map built from the existing
  \`OCR_<LANGUAGE>\` exports.

Example apps (22 files, ~150 substitutions) migrated by script. bare-rn
demo swapped from \`llama3_2_1b\` to \`lfm2_5_1_2b_instruct\` per Kuba's note.
Docs rewritten with the new syntax + TTS + OCR sections.

Relaxes the project's \`camelcase\` rule with \`properties: 'never'\` so the
lowercase snake_case keys in \`models\` (which mirror the \`.pte\` filename
convention) pass without per-file disables. Variable and function names
still require camelCase.
Per Kuba's review on #1148 — hoist a camelCase alias for any group used
≥ 2 times in a file, e.g.

  const instanceSegmentation = models.instance_segmentation;
  const objectDetection = models.object_detection;

Then \`models.instance_segmentation.yolo26n_seg()\` becomes
\`instanceSegmentation.yolo26n_seg()\`. Applied to 14 files where it actually
reduces noise.

Skips aliasing when the camelCase name would shadow an existing local
identifier — common in the LLM/STT/embeddings screens where \`llm\`,
\`speechToText\`, \`imageEmbedding\` etc. already name hook return values
or temporaries.
…version

The 0.8.x doc linked at \`/docs/next/api-reference/variables/MODEL_REGISTRY\`,
which broke once the next-version docs renamed the variable to \`models\`.
Use a relative path within the 0.8.x version so the link is independent of
later renames.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature PRs that implement a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Model constants grouped by type

3 participants