llama.rn

repository·main·Indexed 21 days ago

https://github.com/mybigday/llama.rn

A React Native binding for llama.cpp that enables high-performance LLM inference on iOS and Android devices with GPU/NPU acceleration. It supports multimodal vision/audio, speculative decoding, and structured output. The library provides a JSI-based API for managing model loading, context initialization via LlamaContext, and text completion using CompletionParams.

Tokens
14.9K
Snippets
58
Records
71
Agent score
74%

What's inside llama.rn

  1. Overview of llama.rn API surface

    main

    The llama.rn API provides a high-performance interface for running Llama models in React Native environments. The API is organized into several key categories:

    • Classes: Core logic is encapsulated in classes like LlamaContext.
    • Functions: Global lifecycle and utility functions such as initLlama, loadLlamaModelInfo, and releaseAllLlama.
    • Type Aliases: Extensive typing for parameters and results, including CompletionParams, ContextParams, EmbeddingParams, and various Native* types for low-level interaction.
    • Variables: Global constants like BuildInfo and RNLLAMA_MTMD_DEFAULT_MEDIA_MARKER.

    Developers can use these components to manage model loading, context initialization, text completion, embeddings, and multimodal processing.

  2. Explore llama.rn example capabilities

    main

    The RNLlamaExample project demonstrates several core LLM capabilities. You can find the implementation details for each feature in the following files:

    Model configurations and constants are located in src/utils/constants.ts.

  3. How the llama.cpp Jinja Engine works

    main

    The llama.cpp Jinja Engine is a C++ implementation of the Jinja template engine designed for processing chat templates. It consists of four main components:

    1. jinja::lexer: A predictive parser that converts Jinja source code into a list of tokens. Unlike other implementations, it processes source as-is to allow for precise source tracing during errors.
    2. jinja::parser: Consumes tokens and compiles them into a jinja::program (an Abstract Syntax Tree or AST).
    3. jinja::runtime: Executes the compiled jinja::program using a provided context. It traverses the AST by recursively calling execute(ctx) on each statement or expression.
    4. jinja::value: Defines the primitive types (int, float, bool, string, array, object, none, undefined) and built-in functions. It uses shared_ptr to manage values and allow referencing within Objects and Arrays.
  4. Understand the llama.rn C++ source structure

    main

    The C++ codebase is a hybrid of llama.cpp and llama.rn specific extensions. Most files are mirrored from the llama.cpp submodule and are updated via the npm run bootstrap command.

    Important: Do not edit mirrored llama.cpp files directly. If you need to modify llama.cpp behavior, you should create a patch in scripts/patches/ so that the changes are reapplied during the next bootstrap process.

    To avoid symbol collisions with other native modules, the bootstrap script prefixes llama/ggml symbols with LM_ or lm_.

  5. How multimodal (Vision & Audio) support works

    main

    Multimodal support allows models to process images and audio alongside text.

    Workflow:

    1. Initialize Context: Create the base model context. Crucial: You must set ctx_shift: false to maintain media token positioning.
    2. Initialize Multimodal: Call context.initMultimodal with the path to your mmproj (projector) file.
    3. Verify: Use context.isMultimodalEnabled() and context.getMultimodalSupport() to check capabilities.
    4. Inference: Pass media via structured messages (e.g., image_url or input_audio).
    5. Cleanup: Call context.releaseMultimodal() when finished.

    Supported Formats:

    • Images: JPEG, PNG, BMP, GIF, TGA, HDR, PIC, PNM, or Base64 data URLs. (Local file paths or Base64; HTTP URLs not yet supported).
    • Audio: WAV, MP3, or Base64 data URLs. (Local file paths or Base64; HTTP URLs not yet supported).
    const context = await initLlama({
      model: 'path/to/model.gguf',
      ctx_shift: false, // Required for multimodal
    })
    
    const success = await context.initMultimodal({
      path: 'path/to/mmproj.gguf',
      use_gpu: true,
    })
    
    if (success) {
      // Use context.completion with media content...
    }
  6. Use parallel processing with LlamaContext.parallel

    main

    The parallel property provides a namespace for performing non-blocking operations using a queue. This allows you to trigger multiple requests (like completions, embeddings, or reranking) without blocking the main thread.

    Key operations include:

    • enable(config?): Enables parallel processing. config can include n_batch and n_parallel.
    • disable(): Disables parallel processing.
    • getStatus(): Returns the current ParallelStatus.
    • subscribeToStatus(callback): Subscribes to status updates. Returns a remove function to unsubscribe.
    • completion(params, onToken?): Queues a completion request. Returns a Promise containing a requestId and a stop function to cancel the specific request.
    • embedding(text, params?): Queues an embedding request.
    • rerank(query, documents, params?): Queues a reranking request.
    // Example: Enabling parallel processing and running a completion
    await context.parallel.enable({ n_parallel: 2 });
    
    const { requestId, stop, promise } = await context.parallel.completion(
      { prompt: 'Hello, how are you?' },
      (id, token) => console.log(`Token received: ${token.text}`)
    );
    
    const result = await promise;
    // To cancel this specific request:
    await stop();
  7. Configure speculative decoding with NativeSpeculativeConfig

    main

    Speculative decoding speeds up generation by using a smaller draft model to predict tokens. NativeSpeculativeConfig can be passed to speculative in completion parameters.

    Supported types (NativeSpeculativeType):

    • "none"
    • "draft-mtp"
    • "mtp" (for recurrent/hybrid models like Qwen MTP)

    If using a separate draft model, provide a NativeSpeculativeParams object containing the draft.path or draft.model.

  8. Identify llama.rn specific C++ components

    main

    Core llama.rn functionality is contained in files prefixed with rn-*. These files provide the wrappers and logic required for the React Native integration:

    • rn-llama.*: Manages the context wrapper and lifecycle.
    • rn-completion.*: Handles the legacy completion flow.
    • rn-slot.* and rn-slot-manager.*: Manages parallel decoding and queueing.
    • rn-mtmd.hpp: Provides multimodal (vision/audio) helpers.
    • rn-tts.*: Integrates TTS (Text-to-Speech) and vocoder functionality.
    • rn-common.hpp: Contains shared helpers for tokenization, rerank formatting, and other utilities.

    JSI (JavaScript Interface) bindings that connect the JavaScript layer to these C++ components are located in cpp/jsi/.

  9. Enable Input Marking for security against token injection

    main

    To prevent malicious users from injecting special tokens (like <|end|>) into a chat template, the engine uses jinja::string to wrap std::string and preserve origin metadata via an is_input flag.

    When is_input is true, downstream applications (like llama-server) can identify that a string originated from user input and treat it differently than template-generated tokens.

    How to enable Input Marking

    You can activate this feature in two ways:

    1. Via JSON conversion: Call global_from_json with the parameter mark_input = true.
    2. Manually: Manually invoke value.val_str.mark_input() when creating string values.

    Flag Propagation Rules

    • One-to-one transformations (e.g., uppercase, lowercase): The is_input flag is preserved.
    • One-to-many transformations (e.g., split): The resulting parts are marked is_input = true only if ALL input parts were marked is_input.
    • Many-to-one transformations (e.g., join): Follows the same logic as one-to-many.
    • Concatenation: String parts are appended as-is, and the new string preserves the is_input flag based on its components.
  10. Configure iOS and Android for llama.rn

    main

    iOS

    After installation, run npx pod-install. To build from source instead of using pre-built binaries, set RNLLAMA_BUILD_FROM_SOURCE to 1 in your Podfile.

    Android

    If Proguard is enabled, add the following rule to android/app/proguard-rules.pro:

    -keep class com.rnllama.** { *; }

    To build from source instead of using pre-built libraries, set rnllamaBuildFromSource to true in android/gradle.properties.

    Android GPU/NPU Acceleration

    OpenCL (GPU)

    • Target devices with OpenCL-capable GPUs (e.g., Qualcomm Adreno 700+).
    • Add <uses-native-library android:name="libOpenCL.so" android:required="false" /> to your app manifest.
    • Set n_gpu_layers > 0 in initLlama.

    Hexagon (NPU - Experimental)

    • Target devices with HTP (Qualcomm SM8450+ / Snapdragon 8 Gen 1 or newer).
    • Add <uses-native-library android:name="libcdsprpc.so" android:required="false" /> to your app manifest.
    • Pass devices: ['HTP0'] (or HTP*) to initLlama.
    • Set n_gpu_layers > 0 in initLlama.
  11. Run the llama.rn example project

    main

    To run the example project, you must first install dependencies and bootstrap the repository from the root directory.

    Prerequisites

    From the root directory of the repository, run:

    npm install && npm run bootstrap

    iOS Setup

    1. Install CocoaPods:
    npm run pods
    1. Run on simulator or device:
    npm run ios
    • To run on a specific physical device:
    npm run ios -- --device "<device name>"
    • To run in Release mode:
    npm run ios -- --mode Release

    Android Setup

    Run the example using:

    npm run android
    • To run in Release mode:
    npm run android -- --mode release
    npm install && npm run bootstrap