diff --git a/README.md b/README.md
index 828ed77..e02f890 100644
--- a/README.md
+++ b/README.md
@@ -1,440 +1,304 @@
-# RunAnywhere React Native Starter App
+# RunAnywhere AI for React Native
-A comprehensive starter app demonstrating the capabilities of the [RunAnywhere SDK](https://www.npmjs.com/org/runanywhere) - a privacy-first, on-device AI SDK for React Native.
+
+
+
-
-
-
+
+
+
+
+
+
+
-## โจ Features
+A starter app for the RunAnywhere on-device AI SDK, written in TypeScript.
-This starter app showcases the core capabilities of the RunAnywhere SDK:
+Six screens, one per feature: chat, vision, tool calling, speech to text, text to speech,
+and a voice agent. Inference runs locally through the SDK's C++ core, so apart from
+downloading the model files there is no server involved. Copy a screen, point it at your own
+model, and you have the shape of a real app.
-### ๐ฌ Chat (LLM Text Generation)
-- Streaming text generation with token-by-token output
-- Performance metrics (tokens/second, total tokens)
-- Cancel generation mid-stream
-- Suggested prompts for quick testing
-- Beautiful chat UI with message bubbles
+Every `@runanywhere/*` package is pinned to 0.20.19.
-### ๐ Vision (VLM Image Understanding)
-- On-device image description with a vision-language model (SmolVLM)
-- Streaming, token-by-token descriptions via `RunAnywhere.processImageStream`
-- Sample images plus custom image URL / local path input
-- Editable prompt and cancel mid-generation
+## What it demonstrates
-### ๐ Tool Calling
-- LLM-driven function calling from natural language
-- Structured tool definitions and execution
+Six feature screens, each one wired to a single SDK entry point.
-### ๐ค Speech-to-Text (STT)
-- Real-time audio recording
-- On-device transcription using Whisper models
-- Audio level visualization
-- Transcription history
-- Privacy-first: all processing happens on device
+| Screen | What it does | SDK call |
+|--------|--------------|----------|
+| Chat | Streams a completion token by token, reports tokens/second, cancels mid-stream | `RunAnywhere.llm.generateStream` |
+| Vision | Describes an image picked from samples, a URL, or a local file path | `RunAnywhere.vlm.generateStream` |
+| Tool calling | Runs a prompt against three demo tools (weather, clock, calculator) and logs every call and result | `generateWithTools`, `RunAnywhere.llm.tools.register` |
+| Speech to text | Records 16 kHz mono WAV and transcribes it | `RunAnywhere.stt.transcribe` |
+| Text to speech | Synthesizes and plays text at an adjustable speech rate | `RunAnywhere.tts.speak` |
+| Voice pipeline | Listen, think, speak loop driven by the session event stream | `RunAnywhere.voice.createSession` |
-### ๐ Text-to-Speech (TTS)
-- Neural voice synthesis with Piper TTS
-- Adjustable speech rate (0.5x - 2.0x)
-- Sample texts for quick testing
-- Audio playback controls
-- High-quality, natural-sounding voices
+The home screen is navigation plus a summary of the configured models.
-### โจ Voice Pipeline (Voice Agent)
-- Full voice assistant experience
-- Seamless integration: Speak โ Transcribe โ Generate โ Speak
-- Real-time status updates
-- Conversation history
-- Complete end-to-end voice interaction
+Two things do reach the network at runtime: the weather tool calls wttr.in, and the vision
+screen downloads an image if you give it a URL. Everything else works with the network off
+once the models are on disk.
-## ๐ฆ SDK Packages Used
+## Requirements
-This app uses three RunAnywhere packages:
+- Node 18 or newer.
+- Yarn 3.6.1. `packageManager` is set in `package.json`, so Corepack picks the right version.
+ `yarn.lock` is the lockfile of record; running `npm install` rewrites it into the
+ incompatible Yarn Classic format.
+- iOS: macOS, a recent Xcode, CocoaPods. The deployment target is 17.5.
+- Android: Android Studio, JDK 17, compile and target SDK 36, build tools 36.0.0,
+ NDK 28.0.13004108. Minimum supported device is API 24.
+- A physical device. Models are slow on simulators, and the MLX and Hexagon NPU backends
+ only exist on real hardware.
-| Package | Purpose | NPM |
-|---------|---------|-----|
-| `@runanywhere/core` | Core SDK with infrastructure | [View on NPM](https://www.npmjs.com/package/@runanywhere/core) |
-| `@runanywhere/llamacpp` | LLM backend (LlamaCpp) | [View on NPM](https://www.npmjs.com/package/@runanywhere/llamacpp) |
-| `@runanywhere/onnx` | STT/TTS/VAD backend (ONNX) | [View on NPM](https://www.npmjs.com/package/@runanywhere/onnx) |
-
-## ๐ Getting Started
-
-### Quick Start
+## Setup
```bash
-# Clone and install
git clone https://github.com/RunanywhereAI/react-native-starter-app.git
cd react-native-starter-app
yarn install
-
-# iOS (requires pod install first)
-cd ios && pod install && cd ..
-npx react-native run-ios
-
-# Android (no additional setup needed)
-npx react-native run-android
```
-### Prerequisites
-
-- **Node.js** 18 or higher
-- **Yarn** (Berry, e.g. via Corepack) โ `yarn.lock` is the lockfile of record. Do not
- run `npm install` in this project: npm rewrites `yarn.lock` into the incompatible
- Yarn Classic format.
-- **React Native CLI** development environment ([setup guide](https://reactnative.dev/docs/environment-setup))
-- **iOS:** Xcode 14+, CocoaPods, macOS
-- **Android:**
- - Android Studio
- - JDK 17+
- - Android SDK 36 (compileSdk)
- - NDK 27.1.12297006 (install via Android Studio โ SDK Manager โ SDK Tools โ NDK)
- - Build Tools 36.0.0
-- **Physical device recommended** for best performance (AI models run slowly on simulators)
-
-### Installation
-
-1. **Clone the repository**
- ```bash
- git clone https://github.com/RunanywhereAI/react-native-starter-app.git
- cd react-native-starter-app
- ```
-
-2. **Install dependencies**
- ```bash
- yarn install
- ```
- > **Note:** This runs `patch-package` automatically via postinstall to apply necessary compatibility fixes.
-
-3. **iOS Setup**
- ```bash
- cd ios
- pod install
- cd ..
- ```
- > **Known Issue (RN 0.83):** The `@runanywhere` SDK packages use `podspecPath` in their React Native config, which the RN 0.83 CLI no longer allows. To work around this, `automaticPodsInstallation` is set to `false` in `react-native.config.js`. This means you **must always run `pod install` manually** (as shown above) before building for iOS. You may see warnings about `podspecPath` when running `run-ios` โ these are harmless and can be ignored. This will be fixed in a future SDK release.
-
-4. **Android Setup** (verify your environment)
-
- No additional setup is needed if you have Android Studio installed with the required SDK components. To verify:
-
- ```bash
- # Check that ANDROID_HOME is set (should point to your Android SDK)
- echo $ANDROID_HOME
- # Expected: /Users//Library/Android/sdk (macOS) or similar
-
- # Verify ADB is available
- adb --version
-
- # Check installed NDK versions (need 27.1.12297006)
- ls $ANDROID_HOME/ndk/
- ```
-
- If NDK 27 is missing, install it via Android Studio:
- - Open Android Studio โ Settings โ SDK Manager โ SDK Tools tab
- - Check "Show Package Details" โ expand "NDK (Side by side)"
- - Select version **27.1.12297006** and click Apply
-
-5. **Run the app**
-
- **For iOS:**
- ```bash
- npx react-native run-ios
- ```
-
- **For Android:**
- ```bash
- npx react-native run-android
- ```
-
-### Running with Two Terminals (Recommended)
-
-For better control and visibility of logs, run Metro bundler and the app build in separate terminals:
-
-**Terminal 1 - Start Metro Bundler:**
+Pods are not installed automatically (see platform notes), so install them yourself:
+
```bash
-cd react-native-starter-app
-npx react-native start
+cd ios && pod install && cd ..
```
-Wait until you see "Dev server ready", then in a second terminal:
+Then start Metro in one terminal:
-**Terminal 2 - Build & Run the App:**
```bash
-cd react-native-starter-app
-
-# For iOS
-npx react-native run-ios
-
-# For Android
-npx react-native run-android
+yarn start
```
-> **Note:** The first Android build takes 5-10 minutes as it compiles native C++ code. Subsequent builds are much faster.
-
-### Running on Physical Android Device
-
-When running on a physical Android device, you need to set up port forwarding for the Metro bundler:
+and build in another:
```bash
-# Connect your device via USB and verify it's detected
-adb devices
-
-# Set up port forwarding (required for each USB session)
-adb reverse tcp:8081 tcp:8081
-
-# Start Metro bundler in one terminal
-npx react-native start
-
-# Run the app in another terminal
-npx react-native run-android
+yarn ios
+# or
+yarn android
```
-> **Tip:** If you see "Could not connect to development server", run `adb reverse tcp:8081 tcp:8081` again.
-
-### iOS Permissions
+The first Android build compiles native code and takes several minutes. Later builds are
+much faster.
-The app requires microphone access. Permissions are already configured in `ios/RunAnywhereStarter/Info.plist`:
+On a physical Android device, Metro is reached over USB:
-```xml
-NSMicrophoneUsageDescription
-This app needs microphone access for speech recognition and voice agent features
-NSSpeechRecognitionUsageDescription
-This app uses on-device speech recognition to transcribe your voice
+```bash
+adb reverse tcp:8081 tcp:8081
```
-### Android Permissions
+Run that again after every replug, or the app reports "Could not connect to development
+server".
-Required permissions are configured in `android/app/src/main/AndroidManifest.xml`:
+Type checking is the verification gate:
-```xml
-
-
-
-
+```bash
+yarn typecheck
+yarn lint
```
-## ๐๏ธ Architecture
+## Project layout
```
src/
-โโโ App.tsx # Main app entry, SDK initialization
-โโโ theme/
-โ โโโ colors.ts # Color palette and theme
-โโโ services/
-โ โโโ ModelService.tsx # Model management (download, load, state)
-โโโ components/
-โ โโโ FeatureCard.tsx # Home screen feature cards
-โ โโโ ModelLoaderWidget.tsx # Model download/load UI
-โ โโโ ChatMessageBubble.tsx # Chat message UI
-โ โโโ AudioVisualizer.tsx # Audio level visualization
-โโโ screens/
-โ โโโ HomeScreen.tsx # Main navigation screen
-โ โโโ ChatScreen.tsx # LLM chat interface
-โ โโโ SpeechToTextScreen.tsx # STT interface
-โ โโโ TextToSpeechScreen.tsx # TTS interface
-โ โโโ VoicePipelineScreen.tsx # Voice agent interface
-โโโ navigation/
- โโโ types.ts # Navigation type definitions
+ App.tsx SDK init, backend registration, navigation stack
+ screens/ one file per feature screen, plus HomeScreen
+ services/
+ ModelService.tsx model catalog, download and load state, React context
+ VLMService.ts vision model wrapper used by VisionScreen
+ components/ FeatureCard, ModelLoaderWidget, ChatMessageBubble, AudioVisualizer
+ utils/
+ chatSampleTools.ts the three demo tool definitions and their executors
+ mathParser.ts expression evaluator behind the calculator tool
+ theme/colors.ts dark palette (AppColors)
+ navigation/types.ts stack param list
+ react-native-screens-mock.js iOS shim, see platform notes
+
+ios/RunAnywhereStarter/NativeAudioModule.{swift,m}
+android/app/src/main/java/ai/runanywhere/starter/NativeAudioModule.kt
+ WAV recorder used by the speech to text screen
```
-## ๐ค Default Models
+## How the SDK is wired up
-The app comes preconfigured with these models:
+`App.tsx` initializes the SDK once, then registers backends:
-| Model | Modality | Size | Source |
-|-------|----------|------|--------|
-| LiquidAI LFM2 350M Q8_0 | LLM (text generation) | ~400MB | HuggingFace |
-| SmolLM2 360M Q8_0 | LLM (alternative) | ~500MB | HuggingFace |
-| SmolVLM 500M Instruct | VLM (image understanding) | ~600MB | RunAnywhere |
-| Sherpa ONNX Whisper Tiny EN | STT (speech recognition) | ~80MB | RunAnywhere |
-| Piper TTS (US English) | TTS (voice synthesis) | ~100MB | RunAnywhere |
-| Silero VAD | VAD (voice activity detection) | ~2MB | Silero |
-
-## ๐จ Customization
-
-### Using Different Models
-
-You can modify `src/services/ModelService.tsx` to use different models:
-
-```typescript
-// LLM Model - Example with a larger model
-await LlamaCpp.addModel({
- id: 'qwen2-1.5b-q4',
- name: 'Qwen2 1.5B Q4',
- url: 'https://huggingface.co/...',
- memoryRequirement: 1500000000,
+```ts
+await RunAnywhere.initialize({
+ environment: SDKEnvironment.SDK_ENVIRONMENT_DEVELOPMENT,
});
-// STT Model - Example with multilingual support
-await Onnx.addModel({
- id: 'whisper-small-multi',
- name: 'Whisper Small Multilingual',
- url: 'https://...',
- modality: ModelCategory.speechRecognition,
-});
+const { LlamaCPP } = await import('@runanywhere/llamacpp');
+const { ONNX } = await import('@runanywhere/onnx');
+LlamaCPP.register();
+ONNX.register();
```
-### Theming
-
-The app uses a custom dark theme defined in `src/theme/colors.ts`. You can customize:
-
-```typescript
-export const AppColors = {
- primaryDark: '#0A0E1A',
- accentCyan: '#00D9FF',
- accentViolet: '#8B5CF6',
- // ... more colors
-};
+MLX (Apple, physical iOS devices only) and QHexRT (Qualcomm Hexagon NPU) are registered the
+same way but tolerate failure: `register()` resolves to `false` where the backend cannot
+run, so the calls are safe on every platform.
+
+`registerDefaultModels()` in `src/services/ModelService.tsx` then puts the catalog entries
+into the SDK registry. Registration records metadata only. Downloading and loading happen
+when a screen asks for a model, through `RunAnywhere.models.download` and
+`RunAnywhere.models.load`.
+
+### Streaming: manual iteration, not `for await`
+
+Hermes does not support `for await...of` over the SDK's NitroModules async iterables. Every
+stream in this app is drained with an explicit iterator loop instead:
+
+```ts
+const iterator = RunAnywhere.llm
+ .generateStream(text, { maxOutputTokens: 256, temperature: 0.8 })
+ [Symbol.asyncIterator]();
+
+for (;;) {
+ const step = await iterator.next();
+ if (step.done) break;
+ const event = step.value;
+ if (event.type === 'token') {
+ // append event.text
+ }
+}
```
-## ๐ Privacy
+Calling `iterator.return()` cancels the native generation. That is what the stop buttons do
+in the chat and vision screens.
-All AI processing happens **on-device**. No data is sent to external servers. The models are downloaded once and stored locally on the device.
+## Models
-- โ
No internet required after model download
-- โ
All inference runs locally
-- โ
Your conversations never leave your device
-- โ
No API keys or cloud services needed
+`registerDefaultModels()` registers the entries below. The size column is the declared
+memory requirement, not the exact download size.
-## ๐ Troubleshooting
+| Id | Kind | Backend | Size |
+|----|------|---------|------|
+| `qwen3.5-0.8b-q4_k_m` | LLM, the default | llama.cpp | 533 MB |
+| `lfm2.5-230m-q4_k_m` | LLM, alternative | llama.cpp | 186 MB |
+| `smolvlm-500m-instruct-q8_0` | VLM, the default | llama.cpp | 600 MB |
+| `lfm2.5-vl-3b-q4_k_m` | VLM | llama.cpp | 2.3 GB |
+| `mlx-lfm2.5-vl-3b-4bit` | VLM, registered on iOS only | MLX | 2.6 GB |
+| `sherpa-onnx-whisper-tiny.en` | STT | Sherpa | 75 MB |
+| `vits-piper-en_US-lessac-medium` | TTS | Sherpa | 65 MB |
+| `silero-vad` | VAD, used by the voice pipeline | ONNX | 2 MB |
-### Android NDK build fails: `'rac/rac_defaults_generated.h' file not found`
+`MODEL_IDS` at the top of `ModelService.tsx` decides which of these the screens actually
+use. Every loader screen names the model it is about to fetch and who published it, so
+nothing downloads without saying what it is.
-**Known upstream defect in `@runanywhere/core@0.20.18`. iOS is unaffected.**
+To add your own model, copy one of the existing `RunAnywhere.models.register` calls:
-An Android build (`yarn android`, or `cd android && ./gradlew assembleDebug`) fails
-at the NDK compile step with:
+```ts
+import { InferenceFramework } from '@runanywhere/proto-ts/model_types';
-```
-rac_llm_types.h:27:10: fatal error: 'rac/rac_defaults_generated.h' file not found
+await RunAnywhere.models.register({
+ id: 'my-model-q4_k_m',
+ name: 'My Model Q4_K_M',
+ url: 'https://huggingface.co//resolve/main/.gguf',
+ framework: InferenceFramework.INFERENCE_FRAMEWORK_LLAMA_CPP,
+ memoryRequirementBytes: 1_500_000_000,
+});
```
-**Cause.** The published npm tarball ships the Android C headers under
-`android/src/main/jniLibs/include/rac/`, and five of them
-(`rac_{llm,stt,tts,vad,vlm}_types.h`) `#include "rac/rac_defaults_generated.h"`.
-That generated header is missing from the Android header set. It is present in
-the iOS slice (inside `RACommons.xcframework/*/Headers/rac/`), which is why only
-Android breaks.
+Use `archiveUrl` instead of `url` for a `.tar.gz` bundle, or `files: [...]` for a multi-file
+model such as a GGUF paired with its mmproj vision projector. Non-LLM models also need a
+`category`, for example `ModelCategory.MODEL_CATEGORY_SPEECH_RECOGNITION`.
-**Status.** A fix is merged in the `runanywhere-sdks` monorepo but has not been
-republished to npm yet. There is no supported local workaround: the missing file
-is generated from the IDL and must match the prebuilt `.so` ABI exactly, so
-hand-writing it risks silent runtime mismatch rather than a clean compile error.
+## SDK packages
-**What to do.** Build and run the iOS target until a release later than 0.20.18
-is published, then bump `@runanywhere/*` to it. Do not pin back to 0.20.17 to
-dodge this; other parts of this app target the 0.20.18 API surface.
+| Package | Role |
+|---------|------|
+| `@runanywhere/core` | SDK lifecycle and every capability API |
+| `@runanywhere/proto-ts` | generated protobuf types (`ModelCategory`, `InferenceFramework`, `ToolDefinition`) |
+| `@runanywhere/llamacpp` | GGUF LLM and VLM inference |
+| `@runanywhere/onnx` | Sherpa ONNX speech: STT, TTS, VAD |
+| `@runanywhere/mlx` | Apple MLX, physical iOS devices |
+| `@runanywhere/qhexrt` | Qualcomm Hexagon NPU, Android arm64 only |
-### "Could not connect to development server" (Android)
-This happens on physical Android devices because they can't reach `localhost` on your computer.
+All six are pinned to the same version. The SDK bridges to its C++ core through
+NitroModules, a JSI HybridObject, so `react-native-nitro-modules` is a required peer
+dependency.
-```bash
-# Set up port forwarding
-adb reverse tcp:8081 tcp:8081
+## Platform notes
-# Verify Metro is running
-curl http://localhost:8081/status # Should return "packager-status:running"
-```
-
-### CMake Error: "add_subdirectory given source which is not an existing directory"
-This happens when codegen hasn't run yet. Simply run the build again:
-
-```bash
-cd android && ./gradlew assembleDebug
-```
+### iOS builds against the old architecture
-The second run will succeed as codegen completes.
-
-### Models not downloading
-- Check your internet connection
-- Ensure sufficient storage space (models can be 100MB-1GB)
-- Check iOS/Android permissions
-- Clear app data and try again
-
-### Microphone not working
-- Grant microphone permission in device settings
-- Restart the app after granting permission
-- On Android, check if permission is granted in AndroidManifest.xml
-
-### Low performance
-- Smaller models (like SmolLM2 360M) work better on mobile devices
-- Close other apps to free up memory
-- Use quantized models (Q4/Q8) for better performance
-- Ensure you're running on a physical device (simulators are slow)
-
-### Build errors
-- Clear cache: `cd android && ./gradlew clean` or `cd ios && rm -rf Pods Podfile.lock`
-- Reinstall dependencies: `rm -rf node_modules && yarn install`
-- For iOS: `cd ios && pod install --repo-update`
-- For Android: Delete `android/app/build` and `android/.gradle` folders, then rebuild
-
-### Android NDK not found
-If you see errors about NDK not found:
-```bash
-# Check if NDK 27 is installed
-ls ~/Library/Android/sdk/ndk/
+`ios/Podfile` passes `new_arch_enabled => false` and its post-install hook strips
+`RCT_NEW_ARCH_ENABLED` from the pod targets. Android runs the opposite way, with
+`newArchEnabled=true` in `android/gradle.properties`. The asymmetry is deliberate:
+`react-native-screens` crashes under the new architecture on RN 0.83's iOS side.
-# If missing, install via Android Studio SDK Manager or:
-sdkmanager "ndk;27.1.12297006"
-```
+Three consequences worth knowing before you edit navigation code:
-### Android SDK location not found
-Ensure `local.properties` exists in the `android/` folder with your SDK path:
-```properties
-sdk.dir=/Users//Library/Android/sdk
-```
-This file is auto-generated when you open the project in Android Studio.
+- `metro.config.js` redirects every `react-native-screens` import on iOS to
+ `src/react-native-screens-mock.js`, which swaps the native screen components for plain
+ views.
+- `App.tsx` uses `@react-navigation/stack` with JS animations rather than native-stack.
+- `react-native.config.js` also disables `react-native-sound` on iOS.
-### Patches not applied
-If you see build errors related to `react-native-nitro-modules`, ensure patches are applied:
+### Pods must be installed by hand
-```bash
-npx patch-package
-```
+`react-native.config.js` sets `automaticPodsInstallation: false`, so `yarn ios` will build
+whatever pods are already installed rather than refreshing them. Run `pod install` yourself
+after `yarn install` and after any dependency change, or the build links against a stale
+Pods tree.
-This should run automatically via `postinstall`, but you can run it manually if needed.
+### Audio capture is owned by the app, not the SDK
-## ๐ Documentation
+For the speech to text screen the app records its own audio: `NativeAudioModule` (Swift on
+iOS, Kotlin on Android) writes 16 kHz mono WAV and hands the bytes to
+`RunAnywhere.stt.transcribe`. The voice pipeline screen works differently.
+`RunAnywhere.voice.createSession` opens the microphone, segments utterances and plays the
+replies itself, and the screen only renders the event stream.
-- [RunAnywhere SDK Documentation](https://docs.runanywhere.ai)
-- [React Native Documentation](https://reactnative.dev)
-- [API Reference](https://docs.runanywhere.ai/api)
+### Permissions
-## ๐ค Contributing
+iOS declares `NSMicrophoneUsageDescription` and `NSSpeechRecognitionUsageDescription` in
+`ios/RunAnywhereStarter/Info.plist`. Android declares `INTERNET` and `RECORD_AUDIO` in
+`android/app/src/main/AndroidManifest.xml`, and the speech to text screen requests
+`RECORD_AUDIO` at runtime.
-We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
+## Troubleshooting
-## ๐ License
+A model will not download. Check connectivity and free space. The 3B vision models are over
+two gigabytes each. Progress comes straight from the SDK, so a frozen bar usually means a
+stalled request rather than a stuck UI.
-This starter app is provided under the MIT License. The RunAnywhere SDK is licensed under the [RunAnywhere License](https://runanywhere.ai/license).
+The microphone does nothing. Grant the permission and restart the app. On iOS the decision
+is cached per install.
-For commercial licensing inquiries, contact: san@runanywhere.ai
+Generation is slow. Pick the smallest model that does the job, close other apps, and run on
+a physical device rather than a simulator.
-## ๐ Support
+Android SDK location not found. Create `android/local.properties` containing
+`sdk.dir=/path/to/Android/sdk`. Android Studio writes it on first open.
-- **GitHub Issues**: [Report bugs](https://github.com/RunanywhereAI/runanywhere-sdks/issues)
-- **Email**: san@runanywhere.ai
-- **Documentation**: [runanywhere.ai](https://runanywhere.ai)
-- **Discord**: [Join our community](https://discord.gg/runanywhere)
+NDK not found. `ls $ANDROID_HOME/ndk/` should list `28.0.13004108`. Install it from Android
+Studio's SDK Manager under SDK Tools, or with `sdkmanager "ndk;28.0.13004108"`.
-## ๐ฏ Next Steps
+Clean rebuild. `rm -rf node_modules && yarn install`, then `cd android && ./gradlew clean`
+or `cd ios && rm -rf Pods Podfile.lock && pod install`.
-1. **Explore the code**: Check out each screen to understand how the SDK works
-2. **Try different models**: Swap in your own models to see what works best
-3. **Build your app**: Use this as a foundation for your own AI-powered app
-4. **Share feedback**: Let us know what you think and what features you'd like to see
+## The other apps
-## โญ Acknowledgments
+| Platform | Repo |
+| --- | --- |
+| Flutter | [flutter-starter-example](https://github.com/RunanywhereAI/flutter-starter-example) |
+| iOS and macOS | [runanywhere-ios](https://github.com/RunanywhereAI/runanywhere-ios) |
+| Android | [runanywhere-android](https://github.com/RunanywhereAI/runanywhere-android) |
+| Web | [runanywhere-web](https://github.com/RunanywhereAI/runanywhere-web) |
+| Windows | [runanywhere-electron](https://github.com/RunanywhereAI/runanywhere-electron) |
+| SDK monorepo | [runanywhere-sdks](https://github.com/RunanywhereAI/runanywhere-sdks) |
-Built with:
-- [React Native](https://reactnative.dev)
-- [React Navigation](https://reactnavigation.org)
-- [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated)
-- [React Native Linear Gradient](https://github.com/react-native-linear-gradient/react-native-linear-gradient)
+## Support
-Special thanks to the open-source community and the RunAnywhere team!
+- Issues: https://github.com/RunanywhereAI/runanywhere-sdks/issues
+- Documentation: https://docs.runanywhere.ai
+- Email: san@runanywhere.ai
----
+## License
-Made with โค๏ธ by the RunAnywhere team
+The starter app is MIT licensed. The RunAnywhere SDK is licensed separately, see
+https://runanywhere.ai/license. For commercial licensing, contact san@runanywhere.ai.
diff --git a/src/components/ModelLoaderWidget.tsx b/src/components/ModelLoaderWidget.tsx
index e2cf7fd..841615f 100644
--- a/src/components/ModelLoaderWidget.tsx
+++ b/src/components/ModelLoaderWidget.tsx
@@ -11,6 +11,11 @@ import { AppColors } from '../theme';
interface ModelLoaderWidgetProps {
title: string;
subtitle: string;
+ /**
+ * "Qwen3.5 0.8B Q4_K_M ยท Alibaba". Optional so a caller with no single model
+ * behind it (the voice pipeline loads three) can leave it off.
+ */
+ modelCredit?: string;
icon: string;
accentColor: string;
isDownloading: boolean;
@@ -22,6 +27,7 @@ interface ModelLoaderWidgetProps {
export const ModelLoaderWidget: React.FC = ({
title,
subtitle,
+ modelCredit,
accentColor,
isDownloading,
isLoading,
@@ -46,6 +52,9 @@ export const ModelLoaderWidget: React.FC = ({
{title}
{subtitle}
+ {modelCredit ? (
+ {modelCredit}
+ ) : null}
{(isDownloading || isLoading) && (
@@ -125,9 +134,15 @@ const styles = StyleSheet.create({
fontSize: 14,
color: AppColors.textSecondary,
textAlign: 'center',
- marginBottom: 32,
+ marginBottom: 8,
lineHeight: 20,
},
+ modelCredit: {
+ fontSize: 13,
+ fontWeight: '600',
+ textAlign: 'center',
+ marginBottom: 24,
+ },
loadingContainer: {
alignItems: 'center',
marginVertical: 24,
diff --git a/src/screens/ChatScreen.tsx b/src/screens/ChatScreen.tsx
index f28debc..2e46ab9 100644
--- a/src/screens/ChatScreen.tsx
+++ b/src/screens/ChatScreen.tsx
@@ -13,7 +13,7 @@ import LinearGradient from 'react-native-linear-gradient';
import { RunAnywhere } from '@runanywhere/core';
import type { GenerationEvent, GenerationResult } from '@runanywhere/core';
import { AppColors } from '../theme';
-import { useModelService } from '../services/ModelService';
+import { useModelService, MODEL_CREDITS } from '../services/ModelService';
import { ChatMessageBubble, ChatMessage, ModelLoaderWidget } from '../components';
export const ChatScreen: React.FC = () => {
@@ -144,6 +144,7 @@ export const ChatScreen: React.FC = () => {
if (!modelService.isLLMLoaded) {
return (
{
if (!modelService.isSTTLoaded) {
return (
{
if (!modelService.isTTSLoaded) {
return (
{
if (!modelService.isLLMLoaded) {
return (
{
if (!modelService.isVLMLoaded) {
return (
= ({ chil
* runanywhere-sdks/examples/react-native/RunAnywhereAI/src/services/ModelCatalogBootstrap.ts
*/
export const registerDefaultModels = async () => {
- // LLM Model - LiquidAI LFM2 350M (fast, efficient, great for mobile)
+ // LLM Model - Qwen3.5 0.8B, the smallest current-generation chat model.
await RunAnywhere.models.register({
id: MODEL_IDS.llm,
- name: 'LiquidAI LFM2 350M Q8_0',
- url: 'https://huggingface.co/LiquidAI/LFM2-350M-GGUF/resolve/main/LFM2-350M-Q8_0.gguf',
+ name: 'Qwen3.5 0.8B Q4_K_M',
+ url: 'https://huggingface.co/unsloth/Qwen3.5-0.8B-GGUF/resolve/main/Qwen3.5-0.8B-Q4_K_M.gguf',
framework: InferenceFramework.INFERENCE_FRAMEWORK_LLAMA_CPP,
- memoryRequirementBytes: 400_000_000,
+ memoryRequirementBytes: 900_000_000,
});
- // Also add SmolLM2 as alternative smaller model
+ // A smaller alternative for low-memory devices.
await RunAnywhere.models.register({
- id: 'smollm2-360m-q8_0',
- name: 'SmolLM2 360M Q8_0',
- url: 'https://huggingface.co/prithivMLmods/SmolLM2-360M-GGUF/resolve/main/SmolLM2-360M.Q8_0.gguf',
+ id: 'lfm2.5-230m-q4_k_m',
+ name: 'LiquidAI LFM2.5 230M Q4_K_M',
+ url: 'https://huggingface.co/LiquidAI/LFM2.5-230M-GGUF/resolve/main/LFM2.5-230M-Q4_K_M.gguf',
framework: InferenceFramework.INFERENCE_FRAMEWORK_LLAMA_CPP,
- memoryRequirementBytes: 500_000_000,
+ memoryRequirementBytes: 190_000_000,
});
// VLM Model - SmolVLM 500M (ultra-lightweight vision-language model, ~600MB)
diff --git a/src/services/modelOrg.ts b/src/services/modelOrg.ts
new file mode 100644
index 0000000..acd50d7
--- /dev/null
+++ b/src/services/modelOrg.ts
@@ -0,0 +1,72 @@
+/**
+ * The publisher behind a model, so the loader can say who made the thing it is
+ * about to download instead of just "the language model".
+ *
+ * The rule table is a hand-kept copy of the same table in iOS
+ * (`ModelOrgCatalog`), Android (`ModelTaxonomy`), Web, Electron and Flutter.
+ * Five copies is a known cost; the alternative is a field on the catalog row,
+ * which is a commons change. Keep them in step when a family is added.
+ */
+
+export interface ModelOrg {
+ /** Stable key, e.g. "nvidia". */
+ key: string;
+ /** Consumer-facing name, e.g. "NVIDIA". */
+ name: string;
+}
+
+/**
+ * Ordered matchers against the lowercased "id + name" haystack. First match
+ * wins, so a specific publisher precedes the family it would otherwise be
+ * swallowed by: NVIDIA before Meta so Nemotron stays NVIDIA, DeepSeek before
+ * Alibaba so the R1 Qwen distills stay DeepSeek.
+ */
+const ORG_MATCHERS: ReadonlyArray<{ key: string; name: string; test: RegExp }> = [
+ {
+ key: 'nvidia',
+ name: 'NVIDIA',
+ test: /nemotron|nemoguard|cosmos|canary|parakeet|nv[_-]embed|nv_rerank|nvidia|sortformer/,
+ },
+ { key: 'deepseek', name: 'DeepSeek', test: /deepseek/ },
+ { key: 'prism', name: 'Prism', test: /bonsai|prismml|prism-?ml/ },
+ { key: 'deepgrove', name: 'Deepgrove', test: /maple/ },
+ { key: 'ibm', name: 'IBM', test: /granite/ },
+ // `fara` rides with Microsoft's `phi`: Fara1.5 ships mirrored under our own HF
+ // org, so the catalog row names no upstream publisher. Filing it by its own
+ // name beats guessing one into a UI label.
+ { key: 'microsoft', name: 'Microsoft', test: /\bphi\b|fara/ },
+ { key: 'google', name: 'Google', test: /gemma|embeddinggemma|siglip/ },
+ // Muse Glimmer is Meta's, per the catalog row's own name.
+ { key: 'meta', name: 'Meta', test: /llama|muse-glimmer|muse_glimmer/ },
+ { key: 'alibaba', name: 'Alibaba', test: /qwen/ },
+ { key: 'liquid', name: 'Liquid AI', test: /lfm2/ },
+ { key: 'mistral', name: 'Mistral AI', test: /mistral|ministral/ },
+ { key: 'hugging-face', name: 'Hugging Face', test: /smollm|smolvlm/ },
+ { key: 'openai', name: 'OpenAI', test: /whisper/ },
+ { key: 'zhipu', name: 'Zhipu AI', test: /\bglm\b|glm-/ },
+ {
+ key: 'open-source',
+ name: 'Open source',
+ test: /internvl|moonshine|melo|kokoro|kitten|piper|vits|silero|vad|minilm|supertonic|segformer/,
+ },
+];
+
+const FALLBACK_ORG: ModelOrg = { key: 'open-source', name: 'Open source' };
+
+/**
+ * The publisher for one model. Never throws; an unrecognised name reads as
+ * community rather than guessing a company.
+ */
+export function modelOrg(id: string, name: string): ModelOrg {
+ const haystack = `${id} ${name}`.toLowerCase();
+ const match = ORG_MATCHERS.find((org) => org.test.test(haystack));
+ return match ?? FALLBACK_ORG;
+}
+
+/**
+ * "Qwen3.5 0.8B Q4_K_M ยท Alibaba" โ what the loader shows so a reader knows
+ * what is about to land on their device and who made it.
+ */
+export function modelCredit(id: string, name: string): string {
+ return `${name} ยท ${modelOrg(id, name).name}`;
+}