sdk/runanywhere-react-native/packages/core/README.md
Core SDK for RunAnywhere React Native. Foundation package providing the public API, events, model management, and native bridge infrastructure.
@runanywhere/core is the foundation package of the RunAnywhere React Native SDK. It mirrors the Swift SDK's generated proto-byte/native ownership model from sdk/runanywhere-swift/ARCHITECTURE.md while exposing a TypeScript facade. It provides:
RunAnywhere callsThis package is required for all RunAnywhere functionality. Additional capabilities are provided by:
@runanywhere/llamacpp — LLM text generation (GGUF models)@runanywhere/mlx — Apple MLX inference (LLM, VLM, speech, embeddings)@runanywhere/onnx — Speech-to-Text and Text-to-Speechnpm install @runanywhere/core
# or
yarn add @runanywhere/core
Install the Nitro runtime with the package. Other optional peers remain in package metadata for host-app compatibility, but SDK model download/storage does not route through JavaScript file or blob-util helpers.
npm install react-native-nitro-modules
cd ios && pod install && cd ..
If your app uses the SDK's microphone capture (AudioCaptureManager, STT
recording), add a usage description to your app's Info.plist — iOS refuses
microphone access without it:
<key>NSMicrophoneUsageDescription</key>
<string>This app uses the microphone for speech recognition.</string>
No additional setup required. The SDK's library manifest already declares
android.permission.RECORD_AUDIO for microphone capture; your app must still
request the runtime permission (handled automatically by
AudioCaptureManager.requestPermission()).
import { RunAnywhere, SDKEnvironment } from '@runanywhere/core';
// Initialize SDK
await RunAnywhere.initialize({
environment: SDKEnvironment.SDK_ENVIRONMENT_DEVELOPMENT,
});
// Check initialization
const isReady = RunAnywhere.isInitialized;
console.log('SDK ready:', isReady);
// Get SDK version
console.log('Version:', RunAnywhere.version);
Hermes (the default JS engine in React Native since 0.70) does not support
for await...of with NitroModules-backed async iterables. Every SDK API
that returns an AsyncIterable must be consumed with a manual
Symbol.asyncIterator loop:
const stream = RunAnywhere.generateStream(prompt);
const iterator = stream[Symbol.asyncIterator]();
while (true) {
const { value, done } = await iterator.next();
if (done) break;
// handle value
}
Affected surfaces (every public AsyncIterable the core exposes):
| Surface | Yields |
|---|---|
RunAnywhere.generateStream(prompt, options) | LLMStreamEvent (token, completed, failed, ...) |
RunAnywhere.transcribe(audio, options) / transcribeStream(...) | STTStreamEvent |
RunAnywhere.synthesize(text, options) / synthesizeStream(...) | TTSStreamEvent (audio chunks) |
RunAnywhere.processImage(request) | VLMStreamEvent (vision-language tokens) |
RunAnywhere.downloadModel(id, onProgress?) (when consumed as async iterable) | DownloadProgress |
RunAnywhere.voiceAgent.start(...) | VoiceEvent |
Breaking out of the loop (break / return / throw) automatically
unsubscribes the underlying native subscription, so idiomatic "cancel by
breaking" behaviour is preserved. for await only works reliably when
Hermes is disabled (Node, plain JSC).
The RunAnywhere object is the main entry point for all SDK functionality.
// Initialize SDK
await RunAnywhere.initialize({
apiKey?: string, // API key (production/staging)
baseURL?: string, // API base URL
environment?: SDKEnvironment,
debug?: boolean,
});
// Check status
const isInit = RunAnywhere.isInitialized;
const isActive = RunAnywhere.isActive;
// Reset SDK
await RunAnywhere.reset();
| Property | Type | Description |
|---|---|---|
isInitialized | boolean | Whether SDK core initialization has completed |
isActive | boolean | Whether SDK core is active |
areServicesReady | boolean | Whether services are ready |
environment | SDKEnvironment | null | Current environment |
version | string | SDK version |
deviceId | Promise<string> | Persistent device ID |
events | SDK event helpers | Subscribe to the native proto-byte event stream |
// Get available models
const models = await RunAnywhere.listModels();
// Get specific model info
const model = await RunAnywhere.getModel(ModelGetRequest.fromPartial({ modelId: 'model-id' }));
// List downloaded models
const downloaded = await RunAnywhere.downloadedModels();
// Download with progress
const iterator = RunAnywhere.downloadModel('model-id')[Symbol.asyncIterator]();
let next = await iterator.next();
while (!next.done) {
const progress = next.value;
console.log(`${(progress.progress * 100).toFixed(1)}%`);
next = await iterator.next();
}
import { StorageDeleteRequest } from '@runanywhere/proto-ts/storage_types';
// Remove SDK-owned files through storage APIs when needed.
await RunAnywhere.deleteStorage(StorageDeleteRequest.fromPartial({
modelId: 'model-id',
}));
// Get storage info
const storage = await RunAnywhere.getStorageInfo();
console.log('Free:', storage?.device?.freeBytes ?? 0);
console.log('Used:', storage?.device?.usedBytes ?? 0);
// Clear cache
await RunAnywhere.clearCache();
await RunAnywhere.cleanTempFiles();
Register tools that LLMs can invoke during generation. Tool calling enables models to request external actions (API calls, device functions, calculations, etc.) and incorporate the results into their responses.
import { RunAnywhere } from '@runanywhere/core';
RunAnywhere.registerTool(
{
name: 'get_weather',
description: 'Get the current weather for a location',
parameters: [
{
name: 'location',
type: 'string',
description: 'City name or coordinates',
required: true,
},
{
name: 'units',
type: 'string',
description: 'Temperature units',
required: false,
enum: ['celsius', 'fahrenheit'],
},
],
},
async (args) => {
// Example app/tool code may call fetch; SDK internals use native C++ HTTP.
const response = await fetch(`https://api.weather.com?q=${args.location}`);
const data = await response.json();
return { temperature: data.temp, condition: data.condition };
}
);
import { ToolCallFormatName } from '@runanywhere/proto-ts/tool_calling';
const result = await RunAnywhere.generateWithTools(
'What is the weather in San Francisco?',
{
autoExecute: true, // Automatically execute tool calls
maxToolCalls: 3, // Max tool invocations per turn
temperature: 0.7,
maxTokens: 512,
format: ToolCallFormatName.TOOL_CALL_FORMAT_NAME_JSON,
keepToolsAvailable: false, // Remove tools after first call
}
);
console.log('Response:', result.text);
console.log('Tool calls made:', result.toolCalls.length);
console.log('Tool results:', result.toolResults);
// Step-by-step control over tool execution
const result = await RunAnywhere.generateWithTools(prompt, {
autoExecute: false, // Don't auto-execute
});
// Check if the LLM wants to call a tool
if (result.toolCalls.length > 0) {
const toolCall = result.toolCalls[0];
console.log(`LLM wants to call: ${toolCall.name}`);
console.log('Arguments:', toolCall.argumentsJson);
// Execute manually. JS owns only the executor callback; parsing and
// validation stay in native commons.
const toolResult = await RunAnywhere.executeTool(toolCall);
console.log('Tool result:', toolResult.resultJson || toolResult.error);
}
import type { ToolExecutor } from '@runanywhere/core';
import type {
ToolDefinition,
ToolParameter,
ToolCall,
ToolResult,
ToolCallingOptions,
ToolCallingResult,
} from '@runanywhere/proto-ts/tool_calling';
Generate type-safe JSON responses with schema validation.
import { RunAnywhere } from '@runanywhere/core';
// Generate JSON matching a schema
const result = await RunAnywhere.generateStructured(
'Extract the product info: The new Widget Pro costs $29.99 and is available now',
{
type: 'object',
properties: {
name: { type: 'string', description: 'Product name' },
price: { type: 'number', description: 'Price in USD' },
inStock: { type: 'boolean' },
},
required: ['name', 'price'],
},
{ temperature: 0.3, maxTokens: 256 }
);
console.log(result.rawText);
const parsed = await RunAnywhere.extractStructuredOutput(
'John Smith from Acme Corp called about order #12345',
{
type: 'object',
properties: {
person: { type: 'string' },
company: { type: 'string' },
orderId: { type: 'string' },
},
}
);
console.log(parsed.rawText);
import type {
JSONSchema,
StructuredOutputOptions,
StructuredOutputResult,
} from '@runanywhere/proto-ts/structured_output';
All SDK events (initialization, model lifecycle, generation, voice pipeline,
download progress, telemetry, ...) flow through a single native proto-byte
pipe owned by runanywhere-commons. Subscribe via
RunAnywhere.subscribeSDKEvents(...), which decodes the bytes into a
generated SDKEvent proto message before handing them to your callback.
import { RunAnywhere } from '@runanywhere/core';
const unsubscribe = await RunAnywhere.subscribeSDKEvents((event) => {
// `event` is a decoded SDKEvent proto message.
// Its `payload` oneof identifies the concrete event type
// (generation, model, voice, download, telemetry, ...).
console.log('SDK event:', event.payload?.$case, event);
});
// Later, when you no longer care about events:
await unsubscribe();
Audio-session internals may publish local events, but consumers must use
RunAnywhere.subscribeSDKEvents(...) for SDK event observation.
The public registry surface lives on RunAnywhere and mirrors Swift naming:
registerModel, listModels, queryModels, getModel, downloadedModels,
importModel, downloadModel, loadModel(ModelLoadRequest),
unloadModel(ModelUnloadRequest), and currentModel(CurrentModelRequest).
The registry and download state live in native
commons; React Native encodes requests and decodes generated proto responses.
await RunAnywhere.registerModel({
id: 'my-model',
name: 'My Model',
framework: InferenceFramework.INFERENCE_FRAMEWORK_LLAMA_CPP,
url: 'https://...',
});
const models = await RunAnywhere.listModels();
const downloaded = await RunAnywhere.downloadedModels();
const model = await RunAnywhere.getModel(ModelGetRequest.fromPartial({ modelId: 'my-model' }));
const iterator = RunAnywhere.downloadModel('my-model')[Symbol.asyncIterator]();
let next = await iterator.next();
while (!next.done) {
console.log(`Progress: ${next.value.progress * 100}%`);
next = await iterator.next();
}
Use the RunAnywhere storage and model lifecycle methods for SDK-owned data.
Direct host-app file helpers are separate from model management.
const storage = await RunAnywhere.getStorageInfo();
await RunAnywhere.clearCache();
await RunAnywhere.cleanTempFiles();
import {
SDKException,
isSDKException,
asSDKException,
ErrorCode,
ErrorCategory,
} from '@runanywhere/core';
try {
await RunAnywhere.generate('Hello');
} catch (error) {
if (isSDKException(error)) {
console.log('Code:', error.code); // ErrorCode (proto enum)
console.log('Category:', error.category); // ErrorCategory (proto enum)
console.log('cAbiCode:', error.cAbiCode); // optional negative rac_result_t
console.log('Context:', error.context); // optional ErrorContext
} else {
// Coerce non-SDKException values (Error / string / unknown) into one.
const sdkError = asSDKException(error);
console.log('Code:', sdkError.code);
}
}
// Create errors via the static factories on SDKException.
throw SDKException.notInitialized();
throw SDKException.modelNotFound('model-id');
throw SDKException.of(ErrorCode.ERROR_CODE_INVALID_INPUT, 'Prompt is empty');
SDKLogger and LogLevel are part of the internal subpath
(@runanywhere/core/internal) and not the stable root surface — the API may
change between releases. Import from the internal subpath explicitly if you
need them, otherwise prefer console/your own logger plus the EventBus stream
for stable observability.
import { SDKLogger, LogLevel } from '@runanywhere/core/internal';
// Set global log level
RunAnywhere.setLogLevel(LogLevel.LOG_LEVEL_DEBUG);
// Create custom logger
const logger = new SDKLogger('MyModule');
logger.debug('Debug message', { data: 'value' });
logger.info('Info message');
logger.warning('Warning message');
logger.error('Error message', new Error('...'));
Only SDKEnvironment is re-exported from the @runanywhere/core root barrel.
All other generated enums (ExecutionTarget, InferenceFramework,
ModelCategory, ModelFormat, HardwareAcceleration, ComponentState, …)
live in @runanywhere/proto-ts/model_types and should be imported directly
from there.
import { SDKEnvironment } from '@runanywhere/core';
import {
ExecutionTarget,
InferenceFramework,
ModelCategory,
ModelFormat,
HardwareAcceleration,
ComponentState,
} from '@runanywhere/proto-ts/model_types';
The @runanywhere/core root barrel exports SDKInitOptions, ToolExecutor,
EventBus types (EventBusCancellable, SDKEventHandler), and the plugin
loader types (PluginInfo, PluginLoaderCapability). All other interfaces are
proto-generated DTOs and should be imported from @runanywhere/proto-ts/*:
import type {
SDKInitOptions,
ToolExecutor,
EventBusCancellable,
SDKEventHandler,
PluginInfo,
PluginLoaderCapability,
} from '@runanywhere/core';
import type {
// Models
ModelInfo,
StorageInfo,
// Generation
LLMGenerationOptions,
LLMGenerationResult,
PerformanceMetrics,
// Download
DownloadProgress,
} from '@runanywhere/proto-ts/model_types';
import type {
STTOptions,
STTResult,
} from '@runanywhere/proto-ts/stt_options';
import type {
TTSConfiguration,
TTSResult,
} from '@runanywhere/proto-ts/tts_options';
import type {
ToolDefinition,
ToolParameter,
ToolCall,
ToolResult,
RegisteredTool,
ToolCallingOptions,
ToolCallingResult,
JSONSchema,
StructuredOutputOptions,
StructuredOutputResult,
EntityExtractionResult,
ClassificationResult,
} from '@runanywhere/proto-ts/llm_options';
import type {
SDKEvent,
SDKGenerationEvent,
SDKModelEvent,
SDKVoiceEvent,
} from '@runanywhere/proto-ts/sdk_events';
packages/core/
├── src/
│ ├── index.ts # Public package exports (RunAnywhere
│ │ # facade + SDKException + proto re-exports)
│ ├── internal.ts # `@runanywhere/core/internal` provider /
│ │ # plugin plumbing
│ ├── Public/
│ │ ├── RunAnywhere.ts # Main API singleton
│ │ ├── Events/ # EventBus (public)
│ │ └── Extensions/ # API method implementations
│ │ ├── LLM/RunAnywhere+TextGeneration.ts
│ │ ├── LLM/RunAnywhere+ToolCalling.ts
│ │ ├── LLM/RunAnywhere+StructuredOutput.ts
│ │ ├── LLM/RunAnywhere+LoRA.ts
│ │ ├── STT/RunAnywhere+STT.ts
│ │ ├── TTS/RunAnywhere+TTS.ts
│ │ ├── VAD/RunAnywhere+VAD.ts
│ │ ├── VLM/RunAnywhere+VisionLanguage.ts
│ │ ├── VoiceAgent/RunAnywhere+VoiceAgent.ts
│ │ ├── Models/ # Model lifecycle + downloads
│ │ ├── Storage/RunAnywhere+Storage.ts
│ │ ├── Solutions/ # Convenience solutions
│ │ ├── RAG/RunAnywhere+RAG.ts
│ │ ├── RunAnywhere+Hardware.ts
│ │ ├── RunAnywhere+Logging.ts
│ │ └── RunAnywhere+PluginLoader.ts
│ ├── Foundation/
│ │ ├── Constants/ # Config defaults
│ │ ├── Errors/ # SDKException + proto re-exports
│ │ ├── Initialization/ # Init state machine
│ │ └── Logging/ # SDKLogger
│ ├── Adapters/ # VoiceAgent stream adapter
│ ├── Features/
│ │ └── VoiceSession/ # Voice session + audio playback
│ ├── Internal/
│ │ └── Nitro/ # NitroModules accessor wrappers
│ ├── services/
│ │ ├── ProtoBytes.ts # ArrayBuffer/protobuf conversion
│ │ └── Network/ # Telemetry/config wrappers
│ ├── specs/ # Nitro HybridObject TS specs
│ ├── types/ # TypeScript-only call-site types
│ │ ├── models.ts # Registry + init shapes
│ │ └── external.d.ts # External module declarations
│ └── native/ # NitroModules native module access
├── cpp/ # C++ HybridObject bridges
│ ├── HybridRunAnywhereCore.cpp # Core native bridge
│ └── bridges/ # Platform adapters and native ABI bridges
├── ios/ # iOS native module
├── android/ # Android native module
└── nitrogen/ # Generated Nitro specs
This package includes native bindings via Nitrogen/Nitro for:
The package uses the bundled ios/Binaries/RACommons.xcframework staged from
the Swift-shaped native binary layout.
Native libraries (librac_commons.so, librunanywhere_jni.so) are staged into
android/src/main/jniLibs/.
RunAnywhere License. See LICENSE for details.