Skip to main content

React Native SDK

Run AI models on-device in React Native apps for iOS and Android.

Installation

Install the npm package:
npm install @cactus-compute/react-native
# or
yarn add @cactus-compute/react-native
The package includes pre-built native libraries for iOS and Android.

Setup

iOS

No additional setup required. The XCFramework is automatically linked.

Android

No additional setup required. The native libraries are automatically included.

Usage

Basic Completion

import Cactus from '@cactus-compute/react-native';

const runInference = async () => {
  try {
    const model = await Cactus.init('/path/to/model', null, false);
    
    const messages = [
      { role: 'user', content: 'What is the capital of France?' }
    ];
    
    const result = await Cactus.complete(model, messages);
    console.log(result.response);
    
    await Cactus.destroy(model);
  } catch (error) {
    console.error('Error:', error);
  }
};

Completion with Options and Streaming

import Cactus from '@cactus-compute/react-native';

const streamCompletion = async () => {
  const model = await Cactus.init('/path/to/model');
  
  const messages = [
    { role: 'user', content: 'Tell me a story' }
  ];
  
  const options = {
    max_tokens: 256,
    temperature: 0.7,
  };
  
  const result = await Cactus.complete(
    model,
    messages,
    options,
    null,
    (token, tokenId) => {
      console.log('Token:', token);
    }
  );
  
  console.log('Final result:', result);
  
  await Cactus.destroy(model);
};

Audio Transcription

import Cactus from '@cactus-compute/react-native';

const transcribeAudio = async () => {
  const model = await Cactus.init('/path/to/whisper-model');
  
  // From file
  const result = await Cactus.transcribe(model, '/path/to/audio.wav');
  console.log('Transcription:', result.text);
  
  await Cactus.destroy(model);
};

Embeddings

import Cactus from '@cactus-compute/react-native';

const getEmbeddings = async () => {
  const model = await Cactus.init('/path/to/model');
  
  // Text embedding
  const embedding = await Cactus.embed(model, 'Hello, world!', true);
  console.log('Embedding dimension:', embedding.length);
  
  // Image embedding
  const imageEmbedding = await Cactus.imageEmbed(model, '/path/to/image.jpg');
  
  await Cactus.destroy(model);
};

Vector Index

import Cactus from '@cactus-compute/react-native';

const useVectorIndex = async () => {
  const index = await Cactus.indexInit('/path/to/index', 384);
  
  // Add documents
  await Cactus.indexAdd(
    index,
    [1, 2],
    ['First document', 'Second document'],
    [[0.1, 0.2, ...], [0.3, 0.4, ...]],
    null
  );
  
  // Query
  const results = await Cactus.indexQuery(
    index,
    [0.15, 0.25, ...],
    null
  );
  
  console.log('Search results:', results);
  
  await Cactus.indexDestroy(index);
};

API Reference

Init / Lifecycle

Cactus.init(modelPath: string, corpusDir?: string | null, cacheIndex?: boolean): Promise<number>
Cactus.destroy(model: number): Promise<void>
Cactus.reset(model: number): Promise<void>
Cactus.stop(model: number): Promise<void>
Cactus.getLastError(): Promise<string | null>

Completion

interface Message {
  role: 'system' | 'user' | 'assistant';
  content: string;
  images?: string[];
}

interface CompletionOptions {
  max_tokens?: number;
  temperature?: number;
  top_p?: number;
  stop_sequences?: string[];
}

interface CompletionResult {
  success: boolean;
  response: string;
  function_calls: any[];
  cloud_handoff: boolean;
  confidence: number;
  time_to_first_token_ms: number;
  total_time_ms: number;
  prefill_tps: number;
  decode_tps: number;
  prefill_tokens: number;
  decode_tokens: number;
  total_tokens: number;
}

Cactus.complete(
  model: number,
  messages: Message[],
  options?: CompletionOptions | null,
  tools?: any[] | null,
  callback?: (token: string, tokenId: number) => void
): Promise<CompletionResult>

Transcription

interface TranscriptionResult {
  text: string;
  segments?: any[];
}

Cactus.transcribe(
  model: number,
  audioPath?: string | null,
  prompt?: string | null,
  options?: any | null,
  callback?: (token: string, tokenId: number) => void,
  pcmData?: Uint8Array | null
): Promise<TranscriptionResult>

Cactus.streamTranscribeStart(model: number, options?: any | null): Promise<number>
Cactus.streamTranscribeProcess(stream: number, pcmData: Uint8Array): Promise<string>
Cactus.streamTranscribeStop(stream: number): Promise<string>

Embeddings

Cactus.embed(model: number, text: string, normalize: boolean): Promise<Float32Array>
Cactus.imageEmbed(model: number, imagePath: string): Promise<Float32Array>
Cactus.audioEmbed(model: number, audioPath: string): Promise<Float32Array>

Tokenization

Cactus.tokenize(model: number, text: string): Promise<number[]>
Cactus.scoreWindow(model: number, tokens: number[], start: number, end: number, context: number): Promise<any>

VAD / RAG

Cactus.vad(model: number, audioPath?: string | null, options?: any | null, pcmData?: Uint8Array | null): Promise<any>
Cactus.ragQuery(model: number, query: string, topK: number): Promise<any>

Vector Index

Cactus.indexInit(indexDir: string, embeddingDim: number): Promise<number>
Cactus.indexDestroy(index: number): Promise<void>
Cactus.indexAdd(
  index: number,
  ids: number[],
  documents: string[],
  embeddings: number[][],
  metadatas?: string[] | null
): Promise<void>
Cactus.indexDelete(index: number, ids: number[]): Promise<void>
Cactus.indexGet(index: number, ids: number[]): Promise<any>
Cactus.indexQuery(index: number, embedding: number[], options?: any | null): Promise<any>
Cactus.indexCompact(index: number): Promise<void>

Telemetry

Cactus.setTelemetryEnvironment(cacheDir: string): Promise<void>
Cactus.setAppId(appId: string): Promise<void>
Cactus.telemetryFlush(): Promise<void>
Cactus.telemetryShutdown(): Promise<void>

Bundling Models

Models must be accessible at runtime. Here are recommended approaches:

iOS

  1. Add model files to your Xcode project
  2. Ensure they’re included in “Copy Bundle Resources”
  3. Access via bundle path:
import RNFS from 'react-native-fs';

const modelPath = `${RNFS.MainBundlePath}/model`;
const model = await Cactus.init(modelPath);

Android

  1. Place models in android/app/src/main/assets/
  2. Copy to app storage on first launch:
import RNFS from 'react-native-fs';

const copyModel = async () => {
  const modelPath = `${RNFS.DocumentDirectoryPath}/model`;
  
  if (!(await RNFS.exists(modelPath))) {
    await RNFS.copyFileAssets('model', modelPath);
  }
  
  return modelPath;
};

const modelPath = await copyModel();
const model = await Cactus.init(modelPath);

Requirements

  • React Native 0.64+
  • iOS 14.0+
  • Android API 24+ (arm64-v8a)

See Also

C API Reference

Full C API reference underlying the React Native bindings

GitHub Repository

View source code and contribute

Swift SDK

Native Swift alternative for iOS

Kotlin SDK

Native Kotlin alternative for Android