@runanywhere/web 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +795 -0
  2. package/dist/Foundation/ErrorTypes.d.ts +61 -0
  3. package/dist/Foundation/ErrorTypes.d.ts.map +1 -0
  4. package/dist/Foundation/ErrorTypes.js +90 -0
  5. package/dist/Foundation/ErrorTypes.js.map +1 -0
  6. package/dist/Foundation/EventBus.d.ts +171 -0
  7. package/dist/Foundation/EventBus.d.ts.map +1 -0
  8. package/dist/Foundation/EventBus.js +113 -0
  9. package/dist/Foundation/EventBus.js.map +1 -0
  10. package/dist/Foundation/PlatformAdapter.d.ts +101 -0
  11. package/dist/Foundation/PlatformAdapter.d.ts.map +1 -0
  12. package/dist/Foundation/PlatformAdapter.js +417 -0
  13. package/dist/Foundation/PlatformAdapter.js.map +1 -0
  14. package/dist/Foundation/SDKLogger.d.ts +33 -0
  15. package/dist/Foundation/SDKLogger.d.ts.map +1 -0
  16. package/dist/Foundation/SDKLogger.js +82 -0
  17. package/dist/Foundation/SDKLogger.js.map +1 -0
  18. package/dist/Foundation/SherpaONNXBridge.d.ts +134 -0
  19. package/dist/Foundation/SherpaONNXBridge.d.ts.map +1 -0
  20. package/dist/Foundation/SherpaONNXBridge.js +332 -0
  21. package/dist/Foundation/SherpaONNXBridge.js.map +1 -0
  22. package/dist/Foundation/StructOffsets.d.ts +147 -0
  23. package/dist/Foundation/StructOffsets.d.ts.map +1 -0
  24. package/dist/Foundation/StructOffsets.js +161 -0
  25. package/dist/Foundation/StructOffsets.js.map +1 -0
  26. package/dist/Foundation/WASMBridge.d.ts +241 -0
  27. package/dist/Foundation/WASMBridge.d.ts.map +1 -0
  28. package/dist/Foundation/WASMBridge.js +393 -0
  29. package/dist/Foundation/WASMBridge.js.map +1 -0
  30. package/dist/Infrastructure/ArchiveUtility.d.ts +25 -0
  31. package/dist/Infrastructure/ArchiveUtility.d.ts.map +1 -0
  32. package/dist/Infrastructure/ArchiveUtility.js +139 -0
  33. package/dist/Infrastructure/ArchiveUtility.js.map +1 -0
  34. package/dist/Infrastructure/AudioCapture.d.ts +87 -0
  35. package/dist/Infrastructure/AudioCapture.d.ts.map +1 -0
  36. package/dist/Infrastructure/AudioCapture.js +231 -0
  37. package/dist/Infrastructure/AudioCapture.js.map +1 -0
  38. package/dist/Infrastructure/AudioPlayback.d.ts +53 -0
  39. package/dist/Infrastructure/AudioPlayback.d.ts.map +1 -0
  40. package/dist/Infrastructure/AudioPlayback.js +117 -0
  41. package/dist/Infrastructure/AudioPlayback.js.map +1 -0
  42. package/dist/Infrastructure/DeviceCapabilities.d.ts +39 -0
  43. package/dist/Infrastructure/DeviceCapabilities.d.ts.map +1 -0
  44. package/dist/Infrastructure/DeviceCapabilities.js +111 -0
  45. package/dist/Infrastructure/DeviceCapabilities.js.map +1 -0
  46. package/dist/Infrastructure/ExtensionRegistry.d.ts +30 -0
  47. package/dist/Infrastructure/ExtensionRegistry.d.ts.map +1 -0
  48. package/dist/Infrastructure/ExtensionRegistry.js +41 -0
  49. package/dist/Infrastructure/ExtensionRegistry.js.map +1 -0
  50. package/dist/Infrastructure/ModelDownloader.d.ts +98 -0
  51. package/dist/Infrastructure/ModelDownloader.d.ts.map +1 -0
  52. package/dist/Infrastructure/ModelDownloader.js +431 -0
  53. package/dist/Infrastructure/ModelDownloader.js.map +1 -0
  54. package/dist/Infrastructure/ModelLoaderTypes.d.ts +34 -0
  55. package/dist/Infrastructure/ModelLoaderTypes.d.ts.map +1 -0
  56. package/dist/Infrastructure/ModelLoaderTypes.js +12 -0
  57. package/dist/Infrastructure/ModelLoaderTypes.js.map +1 -0
  58. package/dist/Infrastructure/ModelManager.d.ts +219 -0
  59. package/dist/Infrastructure/ModelManager.d.ts.map +1 -0
  60. package/dist/Infrastructure/ModelManager.js +885 -0
  61. package/dist/Infrastructure/ModelManager.js.map +1 -0
  62. package/dist/Infrastructure/ModelRegistry.d.ts +131 -0
  63. package/dist/Infrastructure/ModelRegistry.d.ts.map +1 -0
  64. package/dist/Infrastructure/ModelRegistry.js +122 -0
  65. package/dist/Infrastructure/ModelRegistry.js.map +1 -0
  66. package/dist/Infrastructure/OPFSStorage.d.ts +143 -0
  67. package/dist/Infrastructure/OPFSStorage.d.ts.map +1 -0
  68. package/dist/Infrastructure/OPFSStorage.js +330 -0
  69. package/dist/Infrastructure/OPFSStorage.js.map +1 -0
  70. package/dist/Infrastructure/VLMWorkerBridge.d.ts +211 -0
  71. package/dist/Infrastructure/VLMWorkerBridge.d.ts.map +1 -0
  72. package/dist/Infrastructure/VLMWorkerBridge.js +264 -0
  73. package/dist/Infrastructure/VLMWorkerBridge.js.map +1 -0
  74. package/dist/Infrastructure/VLMWorkerRuntime.d.ts +38 -0
  75. package/dist/Infrastructure/VLMWorkerRuntime.d.ts.map +1 -0
  76. package/dist/Infrastructure/VLMWorkerRuntime.js +503 -0
  77. package/dist/Infrastructure/VLMWorkerRuntime.js.map +1 -0
  78. package/dist/Infrastructure/VideoCapture.d.ts +118 -0
  79. package/dist/Infrastructure/VideoCapture.d.ts.map +1 -0
  80. package/dist/Infrastructure/VideoCapture.js +207 -0
  81. package/dist/Infrastructure/VideoCapture.js.map +1 -0
  82. package/dist/Public/Extensions/DiffusionTypes.d.ts +64 -0
  83. package/dist/Public/Extensions/DiffusionTypes.d.ts.map +1 -0
  84. package/dist/Public/Extensions/DiffusionTypes.js +28 -0
  85. package/dist/Public/Extensions/DiffusionTypes.js.map +1 -0
  86. package/dist/Public/Extensions/EmbeddingsTypes.d.ts +33 -0
  87. package/dist/Public/Extensions/EmbeddingsTypes.d.ts.map +1 -0
  88. package/dist/Public/Extensions/EmbeddingsTypes.js +13 -0
  89. package/dist/Public/Extensions/EmbeddingsTypes.js.map +1 -0
  90. package/dist/Public/Extensions/RunAnywhere+Diffusion.d.ts +44 -0
  91. package/dist/Public/Extensions/RunAnywhere+Diffusion.d.ts.map +1 -0
  92. package/dist/Public/Extensions/RunAnywhere+Diffusion.js +189 -0
  93. package/dist/Public/Extensions/RunAnywhere+Diffusion.js.map +1 -0
  94. package/dist/Public/Extensions/RunAnywhere+Embeddings.d.ts +56 -0
  95. package/dist/Public/Extensions/RunAnywhere+Embeddings.d.ts.map +1 -0
  96. package/dist/Public/Extensions/RunAnywhere+Embeddings.js +240 -0
  97. package/dist/Public/Extensions/RunAnywhere+Embeddings.js.map +1 -0
  98. package/dist/Public/Extensions/RunAnywhere+ModelManagement.d.ts +53 -0
  99. package/dist/Public/Extensions/RunAnywhere+ModelManagement.d.ts.map +1 -0
  100. package/dist/Public/Extensions/RunAnywhere+ModelManagement.js +153 -0
  101. package/dist/Public/Extensions/RunAnywhere+ModelManagement.js.map +1 -0
  102. package/dist/Public/Extensions/RunAnywhere+STT.d.ts +95 -0
  103. package/dist/Public/Extensions/RunAnywhere+STT.d.ts.map +1 -0
  104. package/dist/Public/Extensions/RunAnywhere+STT.js +417 -0
  105. package/dist/Public/Extensions/RunAnywhere+STT.js.map +1 -0
  106. package/dist/Public/Extensions/RunAnywhere+StructuredOutput.d.ts +69 -0
  107. package/dist/Public/Extensions/RunAnywhere+StructuredOutput.d.ts.map +1 -0
  108. package/dist/Public/Extensions/RunAnywhere+StructuredOutput.js +196 -0
  109. package/dist/Public/Extensions/RunAnywhere+StructuredOutput.js.map +1 -0
  110. package/dist/Public/Extensions/RunAnywhere+TTS.d.ts +55 -0
  111. package/dist/Public/Extensions/RunAnywhere+TTS.d.ts.map +1 -0
  112. package/dist/Public/Extensions/RunAnywhere+TTS.js +253 -0
  113. package/dist/Public/Extensions/RunAnywhere+TTS.js.map +1 -0
  114. package/dist/Public/Extensions/RunAnywhere+TextGeneration.d.ts +80 -0
  115. package/dist/Public/Extensions/RunAnywhere+TextGeneration.d.ts.map +1 -0
  116. package/dist/Public/Extensions/RunAnywhere+TextGeneration.js +436 -0
  117. package/dist/Public/Extensions/RunAnywhere+TextGeneration.js.map +1 -0
  118. package/dist/Public/Extensions/RunAnywhere+ToolCalling.d.ts +82 -0
  119. package/dist/Public/Extensions/RunAnywhere+ToolCalling.d.ts.map +1 -0
  120. package/dist/Public/Extensions/RunAnywhere+ToolCalling.js +576 -0
  121. package/dist/Public/Extensions/RunAnywhere+ToolCalling.js.map +1 -0
  122. package/dist/Public/Extensions/RunAnywhere+VAD.d.ts +70 -0
  123. package/dist/Public/Extensions/RunAnywhere+VAD.d.ts.map +1 -0
  124. package/dist/Public/Extensions/RunAnywhere+VAD.js +231 -0
  125. package/dist/Public/Extensions/RunAnywhere+VAD.js.map +1 -0
  126. package/dist/Public/Extensions/RunAnywhere+VLM.d.ts +58 -0
  127. package/dist/Public/Extensions/RunAnywhere+VLM.d.ts.map +1 -0
  128. package/dist/Public/Extensions/RunAnywhere+VLM.js +262 -0
  129. package/dist/Public/Extensions/RunAnywhere+VLM.js.map +1 -0
  130. package/dist/Public/Extensions/RunAnywhere+VoiceAgent.d.ts +49 -0
  131. package/dist/Public/Extensions/RunAnywhere+VoiceAgent.d.ts.map +1 -0
  132. package/dist/Public/Extensions/RunAnywhere+VoiceAgent.js +222 -0
  133. package/dist/Public/Extensions/RunAnywhere+VoiceAgent.js.map +1 -0
  134. package/dist/Public/Extensions/RunAnywhere+VoicePipeline.d.ts +63 -0
  135. package/dist/Public/Extensions/RunAnywhere+VoicePipeline.d.ts.map +1 -0
  136. package/dist/Public/Extensions/RunAnywhere+VoicePipeline.js +168 -0
  137. package/dist/Public/Extensions/RunAnywhere+VoicePipeline.js.map +1 -0
  138. package/dist/Public/Extensions/STTTypes.d.ts +53 -0
  139. package/dist/Public/Extensions/STTTypes.d.ts.map +1 -0
  140. package/dist/Public/Extensions/STTTypes.js +16 -0
  141. package/dist/Public/Extensions/STTTypes.js.map +1 -0
  142. package/dist/Public/Extensions/TTSTypes.d.ts +31 -0
  143. package/dist/Public/Extensions/TTSTypes.d.ts.map +1 -0
  144. package/dist/Public/Extensions/TTSTypes.js +3 -0
  145. package/dist/Public/Extensions/TTSTypes.js.map +1 -0
  146. package/dist/Public/Extensions/ToolCallingTypes.d.ts +78 -0
  147. package/dist/Public/Extensions/ToolCallingTypes.d.ts.map +1 -0
  148. package/dist/Public/Extensions/ToolCallingTypes.js +8 -0
  149. package/dist/Public/Extensions/ToolCallingTypes.js.map +1 -0
  150. package/dist/Public/Extensions/VADTypes.d.ts +30 -0
  151. package/dist/Public/Extensions/VADTypes.d.ts.map +1 -0
  152. package/dist/Public/Extensions/VADTypes.js +8 -0
  153. package/dist/Public/Extensions/VADTypes.js.map +1 -0
  154. package/dist/Public/Extensions/VLMTypes.d.ts +56 -0
  155. package/dist/Public/Extensions/VLMTypes.d.ts.map +1 -0
  156. package/dist/Public/Extensions/VLMTypes.js +24 -0
  157. package/dist/Public/Extensions/VLMTypes.js.map +1 -0
  158. package/dist/Public/Extensions/VoiceAgentTypes.d.ts +42 -0
  159. package/dist/Public/Extensions/VoiceAgentTypes.d.ts.map +1 -0
  160. package/dist/Public/Extensions/VoiceAgentTypes.js +12 -0
  161. package/dist/Public/Extensions/VoiceAgentTypes.js.map +1 -0
  162. package/dist/Public/Extensions/VoicePipelineTypes.d.ts +69 -0
  163. package/dist/Public/Extensions/VoicePipelineTypes.d.ts.map +1 -0
  164. package/dist/Public/Extensions/VoicePipelineTypes.js +9 -0
  165. package/dist/Public/Extensions/VoicePipelineTypes.js.map +1 -0
  166. package/dist/Public/RunAnywhere.d.ts +121 -0
  167. package/dist/Public/RunAnywhere.d.ts.map +1 -0
  168. package/dist/Public/RunAnywhere.js +332 -0
  169. package/dist/Public/RunAnywhere.js.map +1 -0
  170. package/dist/index.d.ts +67 -0
  171. package/dist/index.d.ts.map +1 -0
  172. package/dist/index.js +53 -0
  173. package/dist/index.js.map +1 -0
  174. package/dist/types/LLMTypes.d.ts +48 -0
  175. package/dist/types/LLMTypes.d.ts.map +1 -0
  176. package/dist/types/LLMTypes.js +8 -0
  177. package/dist/types/LLMTypes.js.map +1 -0
  178. package/dist/types/enums.d.ts +144 -0
  179. package/dist/types/enums.d.ts.map +1 -0
  180. package/dist/types/enums.js +159 -0
  181. package/dist/types/enums.js.map +1 -0
  182. package/dist/types/index.d.ts +9 -0
  183. package/dist/types/index.d.ts.map +1 -0
  184. package/dist/types/index.js +8 -0
  185. package/dist/types/index.js.map +1 -0
  186. package/dist/types/models.d.ts +154 -0
  187. package/dist/types/models.d.ts.map +1 -0
  188. package/dist/types/models.js +8 -0
  189. package/dist/types/models.js.map +1 -0
  190. package/dist/workers/vlm-worker.d.ts +9 -0
  191. package/dist/workers/vlm-worker.d.ts.map +1 -0
  192. package/dist/workers/vlm-worker.js +10 -0
  193. package/dist/workers/vlm-worker.js.map +1 -0
  194. package/package.json +62 -0
  195. package/wasm/racommons-webgpu.js +156 -0
  196. package/wasm/racommons-webgpu.wasm +0 -0
  197. package/wasm/racommons.js +126 -0
  198. package/wasm/racommons.wasm +0 -0
  199. package/wasm/sherpa/sherpa-onnx-asr.js +1538 -0
  200. package/wasm/sherpa/sherpa-onnx-glue-original.js +19 -0
  201. package/wasm/sherpa/sherpa-onnx-glue.js +17 -0
  202. package/wasm/sherpa/sherpa-onnx-tts.js +657 -0
  203. package/wasm/sherpa/sherpa-onnx-vad.js +337 -0
  204. package/wasm/sherpa/sherpa-onnx-wave.js +88 -0
  205. package/wasm/sherpa/sherpa-onnx.wasm +0 -0
package/README.md ADDED
@@ -0,0 +1,795 @@
1
+ # RunAnywhere Web SDK
2
+
3
+ On-device AI for the browser. Run LLMs, Speech-to-Text, Text-to-Speech, Vision, and Voice AI locally via WebAssembly -- private, offline-capable, zero server dependencies.
4
+
5
+ <p align="center">
6
+ <a href="#"><img src="https://img.shields.io/badge/WebAssembly-Powered-654FF0?style=flat-square&logo=webassembly&logoColor=white" alt="WebAssembly" /></a>
7
+ <a href="#"><img src="https://img.shields.io/badge/TypeScript-5.6+-3178C6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript 5.6+" /></a>
8
+ <a href="#"><img src="https://img.shields.io/badge/Chrome-96+-4285F4?style=flat-square&logo=googlechrome&logoColor=white" alt="Chrome 96+" /></a>
9
+ <a href="#"><img src="https://img.shields.io/badge/Node.js-18+-339933?style=flat-square&logo=node.js&logoColor=white" alt="Node.js 18+" /></a>
10
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-blue?style=flat-square" alt="License" /></a>
11
+ </p>
12
+
13
+ > **Beta (v0.1.0)** -- This is an early release for testing and feedback. The API surface is stable but may change before v1.0. Not yet recommended for production deployments without thorough testing.
14
+
15
+ ---
16
+
17
+ ## Quick Links
18
+
19
+ - [Architecture Overview](#architecture)
20
+ - [Quick Start](#quick-start)
21
+ - [Building from Source](#building-from-source)
22
+ - [Browser Requirements](#browser-requirements)
23
+ - [Cross-Origin Isolation Headers](#cross-origin-isolation-headers)
24
+ - [Demo App](#demo-app)
25
+ - [FAQ](#faq)
26
+ - [Troubleshooting](#troubleshooting)
27
+
28
+ ---
29
+
30
+ ## Features
31
+
32
+ ### Large Language Models (LLM)
33
+ - On-device text generation with streaming support
34
+ - llama.cpp backend compiled to WASM (Llama, Mistral, Qwen, SmolLM, and other GGUF models)
35
+ - Configurable system prompts, temperature, top-k/top-p, and max tokens
36
+ - Token streaming with real-time callbacks and cancellation
37
+
38
+ ### Speech-to-Text (STT)
39
+ - Offline speech recognition via whisper.cpp and sherpa-onnx (WASM)
40
+ - Multiple model architectures: Whisper, Zipformer, Paraformer
41
+ - Batch transcription from Float32Array audio data
42
+ - Archive-based model loading (matching iOS/Android SDK approach)
43
+
44
+ ### Text-to-Speech (TTS)
45
+ - Neural voice synthesis via sherpa-onnx Piper TTS (WASM)
46
+ - Multiple voice models with configurable parameters
47
+ - PCM audio output (Float32Array) with sample rate metadata
48
+
49
+ ### Voice Activity Detection (VAD)
50
+ - Silero VAD model via sherpa-onnx (WASM)
51
+ - Real-time speech/silence detection from audio streams
52
+ - Speech segment extraction with configurable thresholds
53
+ - Callback-based speech activity events
54
+
55
+ ### Vision Language Models (VLM)
56
+ - Multimodal inference via llama.cpp with mtmd support
57
+ - Accepts RGB pixel data, base64, or file paths
58
+ - Runs in a dedicated Web Worker to keep the UI responsive
59
+ - Supports Qwen2-VL and other VLM architectures
60
+
61
+ ### Voice Pipeline
62
+ - Full VAD -> STT -> LLM (streaming) -> TTS orchestration
63
+ - Callback-driven state transitions (transcription, generation, synthesis)
64
+ - Cancellation support for in-progress generation
65
+
66
+ ### Tool Calling and Structured Output
67
+ - Function calling with typed tool definitions and parameter schemas
68
+ - JSON schema-guided structured generation
69
+ - Hermes-style and generic tool calling formats
70
+
71
+ ### Embeddings
72
+ - On-device vector embedding generation
73
+ - Configurable normalization and pooling strategies
74
+ - Single-text and batch embedding support
75
+
76
+ ### Infrastructure
77
+ - Persistent model storage via Origin Private File System (OPFS)
78
+ - Automatic LRU eviction when storage quota is exceeded
79
+ - In-memory fallback cache for quota-exceeded scenarios
80
+ - Model download with progress tracking and multi-file support
81
+ - Browser capability detection (WebGPU, SharedArrayBuffer, OPFS)
82
+ - Structured logging with configurable log levels via `SDKLogger`
83
+ - Event system via `EventBus` for model lifecycle and SDK events
84
+
85
+ ---
86
+
87
+ ## System Requirements
88
+
89
+ | Component | Minimum | Recommended |
90
+ |-----------|---------|-------------|
91
+ | **Browser** | Chrome 96+ / Edge 96+ | Chrome 120+ / Edge 120+ |
92
+ | **WebAssembly** | Required | Required |
93
+ | **SharedArrayBuffer** | For multi-threaded WASM | Requires Cross-Origin Isolation headers |
94
+ | **WebGPU** | For GPU-accelerated diffusion | Chrome 120+ |
95
+ | **OPFS** | For persistent model storage | All modern browsers |
96
+ | **RAM** | 2GB | 4GB+ for larger models |
97
+ | **Storage** | Variable | Models: 40MB -- 4GB depending on model |
98
+
99
+ ---
100
+
101
+ ## Package Structure
102
+
103
+ The Web SDK is a single npm package. Unlike native SDKs (iOS, Android, React Native, Flutter) which use separate packages per backend, the Web SDK compiles all inference backends into a single WebAssembly binary. Backend selection happens at WASM build time, not at the package level.
104
+
105
+ ```
106
+ @runanywhere/web -- TypeScript API + pre-built WASM (all backends)
107
+ ```
108
+
109
+ The pre-built WASM includes llama.cpp (LLM/VLM), whisper.cpp (STT), and sherpa-onnx (TTS/VAD). Developers who need a smaller WASM binary with specific backends can [build from source](#building-from-source) with selective flags.
110
+
111
+ ---
112
+
113
+ ## Installation
114
+
115
+ ```bash
116
+ npm install @runanywhere/web
117
+ ```
118
+
119
+ ### Serve WASM Files
120
+
121
+ The package includes pre-built WASM files in `node_modules/@runanywhere/web/wasm/`. Configure your bundler to serve these as static assets.
122
+
123
+ **Vite:**
124
+
125
+ ```typescript
126
+ // vite.config.ts
127
+ export default defineConfig({
128
+ assetsInclude: ['**/*.wasm'],
129
+ server: {
130
+ headers: {
131
+ 'Cross-Origin-Opener-Policy': 'same-origin',
132
+ 'Cross-Origin-Embedder-Policy': 'credentialless',
133
+ },
134
+ },
135
+ });
136
+ ```
137
+
138
+ **Webpack:**
139
+
140
+ ```javascript
141
+ // webpack.config.js
142
+ module.exports = {
143
+ module: {
144
+ rules: [
145
+ { test: /\.wasm$/, type: 'asset/resource' },
146
+ ],
147
+ },
148
+ };
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Quick Start
154
+
155
+ ### 1. Initialize the SDK
156
+
157
+ ```typescript
158
+ import { RunAnywhere } from '@runanywhere/web';
159
+
160
+ await RunAnywhere.initialize({ environment: 'development', debug: true });
161
+ ```
162
+
163
+ ### 2. Text Generation (LLM)
164
+
165
+ ```typescript
166
+ import { TextGeneration } from '@runanywhere/web';
167
+
168
+ // Load a GGUF model
169
+ await TextGeneration.loadModel('/models/qwen2.5-0.5b-instruct-q4_0.gguf', 'qwen2.5-0.5b');
170
+
171
+ // Generate
172
+ const result = await TextGeneration.generate('Explain quantum computing briefly.');
173
+ console.log(result.text);
174
+
175
+ // Stream tokens
176
+ for await (const token of TextGeneration.generateStream('Write a haiku about code.')) {
177
+ process.stdout.write(token);
178
+ }
179
+ ```
180
+
181
+ ### 3. Speech-to-Text (STT)
182
+
183
+ ```typescript
184
+ import { STT } from '@runanywhere/web';
185
+
186
+ await STT.loadModel({
187
+ modelId: 'whisper-tiny',
188
+ type: STTModelType.Whisper,
189
+ modelFiles: { encoder: '/models/encoder.onnx', decoder: '/models/decoder.onnx', tokens: '/models/tokens.txt' },
190
+ sampleRate: 16000,
191
+ });
192
+
193
+ const result = await STT.transcribe(audioFloat32Array);
194
+ console.log(result.text);
195
+ ```
196
+
197
+ ### 4. Text-to-Speech (TTS)
198
+
199
+ ```typescript
200
+ import { TTS } from '@runanywhere/web';
201
+
202
+ await TTS.loadVoice({
203
+ voiceId: 'piper-en',
204
+ modelPath: '/models/piper-en.onnx',
205
+ tokensPath: '/models/tokens.txt',
206
+ dataDir: '/models/espeak-ng-data',
207
+ });
208
+
209
+ const result = await TTS.synthesize('Hello from RunAnywhere!');
210
+ // result.audioData is Float32Array, result.sampleRate is the sample rate
211
+ ```
212
+
213
+ ### 5. Voice Activity Detection (VAD)
214
+
215
+ ```typescript
216
+ import { VAD, SpeechActivity } from '@runanywhere/web';
217
+
218
+ await VAD.initialize({ modelPath: '/models/silero_vad.onnx' });
219
+
220
+ VAD.onSpeechActivity((activity) => {
221
+ if (activity === SpeechActivity.Ended) {
222
+ const segment = VAD.popSpeechSegment();
223
+ if (segment) console.log(`Speech: ${segment.samples.length} samples`);
224
+ }
225
+ });
226
+
227
+ // Feed audio chunks from microphone
228
+ VAD.processSamples(audioChunk);
229
+ ```
230
+
231
+ ### 6. Vision Language Model (VLM)
232
+
233
+ ```typescript
234
+ import { VLM, VLMImageFormat } from '@runanywhere/web';
235
+
236
+ await VLM.loadModel('/models/qwen2-vl.gguf', '/models/mmproj.gguf', 'qwen2-vl');
237
+
238
+ const result = await VLM.process(
239
+ { format: VLMImageFormat.RGB, rgbPixels: pixelData, width: 256, height: 256 },
240
+ 'Describe this image.',
241
+ { maxTokens: 100 },
242
+ );
243
+ console.log(result.text);
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Architecture
249
+
250
+ ```
251
+ +---------------------------------------------+
252
+ | TypeScript API |
253
+ | RunAnywhere / TextGeneration / STT / TTS |
254
+ | VAD / VLM / VoicePipeline / Embeddings |
255
+ +---------------------------------------------+
256
+ | WASMBridge + PlatformAdapter |
257
+ | (Emscripten addFunction / ccall / cwrap) |
258
+ +---------------------------------------------+
259
+ | RACommons C++ (compiled to WASM) |
260
+ | - Service Registry - Event System |
261
+ | - Model Management - Lifecycle |
262
+ +---------------------------------------------+
263
+ | Inference Backends (WASM) |
264
+ | - llama.cpp (LLM / VLM) |
265
+ | - whisper.cpp (STT) |
266
+ | - sherpa-onnx (TTS / VAD) |
267
+ +---------------------------------------------+
268
+ ```
269
+
270
+ The Web SDK compiles the **same C++ core** (`runanywhere-commons`) used by the iOS and Android SDKs to WebAssembly via Emscripten. The inference engines (llama.cpp, whisper.cpp, sherpa-onnx) are the same native code running in the browser, with identical vtable dispatch, service registry, and event system.
271
+
272
+ ### Key Components
273
+
274
+ | Layer | Component | Description |
275
+ |-------|-----------|-------------|
276
+ | **Public** | `RunAnywhere` | SDK lifecycle (initialize, shutdown, device capabilities) |
277
+ | **Public** | `TextGeneration` | LLM text generation and streaming |
278
+ | **Public** | `STT` | Speech-to-text transcription |
279
+ | **Public** | `TTS` | Text-to-speech synthesis |
280
+ | **Public** | `VAD` | Voice activity detection |
281
+ | **Public** | `VLM` | Vision-language model inference |
282
+ | **Public** | `VoicePipeline` | STT -> LLM -> TTS orchestration |
283
+ | **Public** | `ToolCalling` | Function calling with typed definitions |
284
+ | **Public** | `StructuredOutput` | JSON schema-guided generation |
285
+ | **Public** | `Embeddings` | Vector embedding generation |
286
+ | **Foundation** | `WASMBridge` | Emscripten module loader and C interop |
287
+ | **Foundation** | `SDKLogger` | Structured logging with configurable levels |
288
+ | **Foundation** | `EventBus` | Typed event system for SDK lifecycle events |
289
+ | **Foundation** | `SDKError` | Typed error hierarchy with error codes |
290
+ | **Infrastructure** | `ModelManager` | Model download, storage, and loading orchestration |
291
+ | **Infrastructure** | `OPFSStorage` | Persistent storage via Origin Private File System |
292
+ | **Infrastructure** | `AudioCapture` | Microphone capture with Web Audio API |
293
+ | **Infrastructure** | `VideoCapture` | Camera capture and frame extraction |
294
+ | **Infrastructure** | `AudioPlayback` | Audio playback via Web Audio API |
295
+ | **Infrastructure** | `VLMWorkerBridge` | Web Worker bridge for off-main-thread VLM inference |
296
+
297
+ ---
298
+
299
+ ## Project Structure
300
+
301
+ ```
302
+ sdk/runanywhere-web/
303
+ +-- packages/
304
+ | +-- core/ # @runanywhere/web npm package
305
+ | +-- src/
306
+ | | +-- Public/ # Public API
307
+ | | | +-- RunAnywhere.ts
308
+ | | | +-- Extensions/
309
+ | | | +-- RunAnywhere+TextGeneration.ts
310
+ | | | +-- RunAnywhere+STT.ts
311
+ | | | +-- RunAnywhere+TTS.ts
312
+ | | | +-- RunAnywhere+VAD.ts
313
+ | | | +-- RunAnywhere+VLM.ts
314
+ | | | +-- RunAnywhere+VoiceAgent.ts
315
+ | | | +-- RunAnywhere+VoicePipeline.ts
316
+ | | | +-- RunAnywhere+ToolCalling.ts
317
+ | | | +-- RunAnywhere+StructuredOutput.ts
318
+ | | | +-- RunAnywhere+Embeddings.ts
319
+ | | | +-- RunAnywhere+Diffusion.ts
320
+ | | | +-- RunAnywhere+ModelManagement.ts
321
+ | | +-- Foundation/ # Core infrastructure
322
+ | | | +-- WASMBridge.ts
323
+ | | | +-- PlatformAdapter.ts
324
+ | | | +-- EventBus.ts
325
+ | | | +-- SDKLogger.ts
326
+ | | | +-- ErrorTypes.ts
327
+ | | | +-- SherpaONNXBridge.ts
328
+ | | +-- Infrastructure/ # Browser services
329
+ | | | +-- ModelManager.ts
330
+ | | | +-- ModelDownloader.ts
331
+ | | | +-- ModelRegistry.ts
332
+ | | | +-- OPFSStorage.ts
333
+ | | | +-- AudioCapture.ts
334
+ | | | +-- AudioPlayback.ts
335
+ | | | +-- VideoCapture.ts
336
+ | | | +-- VLMWorkerBridge.ts
337
+ | | | +-- DeviceCapabilities.ts
338
+ | | | +-- ArchiveUtility.ts
339
+ | | +-- types/ # Shared type definitions
340
+ | +-- wasm/ # WASM build output (generated)
341
+ | +-- dist/ # TypeScript build output (generated)
342
+ +-- wasm/ # Emscripten build system
343
+ | +-- CMakeLists.txt
344
+ | +-- src/wasm_exports.cpp
345
+ | +-- platform/wasm_platform_shims.cpp
346
+ | +-- scripts/
347
+ | +-- build.sh # Main WASM build script
348
+ | +-- setup-emsdk.sh # Emscripten SDK installer
349
+ | +-- build-sherpa-onnx.sh # Sherpa-ONNX WASM build
350
+ +-- package.json # Workspace root
351
+ +-- tsconfig.base.json
352
+ ```
353
+
354
+ ---
355
+
356
+ ## Building from Source
357
+
358
+ Building from source is only required if you want to modify the C++ core or build a custom WASM binary with specific backends. Pre-built WASM files are included in the npm package.
359
+
360
+ ### Prerequisites
361
+
362
+ - [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) v5.0.0+
363
+ - Node.js 18+
364
+ - CMake 3.22+
365
+
366
+ ### Setup Emscripten
367
+
368
+ ```bash
369
+ # One-time setup
370
+ ./wasm/scripts/setup-emsdk.sh
371
+ source ~/emsdk/emsdk_env.sh
372
+ ```
373
+
374
+ ### Build WASM
375
+
376
+ ```bash
377
+ # All backends (LLM + STT + TTS/VAD) -- produces racommons.wasm (~3.6 MB)
378
+ ./wasm/scripts/build.sh --all-backends
379
+
380
+ # Individual backends
381
+ ./wasm/scripts/build.sh --llamacpp # LLM only (llama.cpp)
382
+ ./wasm/scripts/build.sh --whispercpp # STT only (whisper.cpp)
383
+ ./wasm/scripts/build.sh --onnx # TTS/VAD only (sherpa-onnx)
384
+ ./wasm/scripts/build.sh --llamacpp --vlm # LLM + VLM (llama.cpp + mtmd)
385
+
386
+ # WebGPU-accelerated build
387
+ ./wasm/scripts/build.sh --webgpu
388
+
389
+ # Debug build with pthreads
390
+ ./wasm/scripts/build.sh --debug --pthreads --all-backends
391
+
392
+ # Clean rebuild
393
+ ./wasm/scripts/build.sh --clean --all-backends
394
+ ```
395
+
396
+ Build outputs are copied to `packages/core/wasm/`.
397
+
398
+ ### Build TypeScript
399
+
400
+ ```bash
401
+ cd sdk/runanywhere-web
402
+ npm install
403
+ npm run build:ts
404
+ ```
405
+
406
+ Output: `packages/core/dist/index.js` and `packages/core/dist/index.d.ts`.
407
+
408
+ ### Typecheck
409
+
410
+ ```bash
411
+ cd packages/core && npx tsc --noEmit
412
+ ```
413
+
414
+ ---
415
+
416
+ ## Browser Requirements
417
+
418
+ | Feature | Required | Fallback |
419
+ |---------|----------|----------|
420
+ | WebAssembly | Yes | N/A |
421
+ | SharedArrayBuffer | For pthreads (multi-threaded) | Single-threaded mode |
422
+ | Cross-Origin Isolation | For SharedArrayBuffer | Single-threaded mode |
423
+ | WebGPU | For Diffusion backend | N/A (Diffusion unavailable) |
424
+ | OPFS | For persistent model storage | MEMFS (volatile, models re-downloaded each session) |
425
+ | Web Audio API | For microphone capture / playback | N/A |
426
+
427
+ Use `detectCapabilities()` to check browser support at runtime:
428
+
429
+ ```typescript
430
+ import { detectCapabilities } from '@runanywhere/web';
431
+
432
+ const caps = await detectCapabilities();
433
+ console.log('Cross-Origin Isolated:', caps.isCrossOriginIsolated);
434
+ console.log('SharedArrayBuffer:', caps.hasSharedArrayBuffer);
435
+ console.log('WebGPU:', caps.hasWebGPU);
436
+ console.log('OPFS:', caps.hasOPFS);
437
+ ```
438
+
439
+ ---
440
+
441
+ ## Cross-Origin Isolation Headers
442
+
443
+ For multi-threaded WASM (pthreads), your server must set two HTTP headers on every response:
444
+
445
+ ```
446
+ Cross-Origin-Opener-Policy: same-origin
447
+ Cross-Origin-Embedder-Policy: require-corp
448
+ ```
449
+
450
+ These headers enable `SharedArrayBuffer`, which is required for multi-threaded WASM. Without them, `crossOriginIsolated` will be `false` and the SDK falls back to single-threaded mode.
451
+
452
+ **Note:** `require-corp` means all sub-resources (images, scripts, fonts, iframes) must either be same-origin or include a `Cross-Origin-Resource-Policy: cross-origin` header. Plan accordingly for CDN assets.
453
+
454
+ ### Configuration by Platform
455
+
456
+ <details>
457
+ <summary>Nginx</summary>
458
+
459
+ ```nginx
460
+ server {
461
+ listen 443 ssl;
462
+ server_name app.example.com;
463
+
464
+ add_header Cross-Origin-Opener-Policy "same-origin" always;
465
+ add_header Cross-Origin-Embedder-Policy "require-corp" always;
466
+
467
+ types {
468
+ application/wasm wasm;
469
+ }
470
+
471
+ location ~* \.wasm$ {
472
+ add_header Cross-Origin-Opener-Policy "same-origin" always;
473
+ add_header Cross-Origin-Embedder-Policy "require-corp" always;
474
+ add_header Cache-Control "public, max-age=31536000, immutable";
475
+ }
476
+ }
477
+ ```
478
+ </details>
479
+
480
+ <details>
481
+ <summary>Vercel</summary>
482
+
483
+ ```json
484
+ {
485
+ "headers": [
486
+ {
487
+ "source": "/(.*)",
488
+ "headers": [
489
+ { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
490
+ { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }
491
+ ]
492
+ }
493
+ ]
494
+ }
495
+ ```
496
+ </details>
497
+
498
+ <details>
499
+ <summary>Netlify</summary>
500
+
501
+ ```toml
502
+ [[headers]]
503
+ for = "/*"
504
+ [headers.values]
505
+ Cross-Origin-Opener-Policy = "same-origin"
506
+ Cross-Origin-Embedder-Policy = "require-corp"
507
+ ```
508
+ </details>
509
+
510
+ <details>
511
+ <summary>Cloudflare Pages</summary>
512
+
513
+ Create a `_headers` file in the project root:
514
+
515
+ ```
516
+ /*
517
+ Cross-Origin-Opener-Policy: same-origin
518
+ Cross-Origin-Embedder-Policy: require-corp
519
+ ```
520
+ </details>
521
+
522
+ <details>
523
+ <summary>CloudFront (AWS)</summary>
524
+
525
+ Add a **Response Headers Policy** with:
526
+ - `Cross-Origin-Opener-Policy: same-origin`
527
+ - `Cross-Origin-Embedder-Policy: require-corp`
528
+
529
+ Or use a CloudFront Function:
530
+
531
+ ```javascript
532
+ function handler(event) {
533
+ var response = event.response;
534
+ var headers = response.headers;
535
+ headers['cross-origin-opener-policy'] = { value: 'same-origin' };
536
+ headers['cross-origin-embedder-policy'] = { value: 'require-corp' };
537
+ return response;
538
+ }
539
+ ```
540
+ </details>
541
+
542
+ <details>
543
+ <summary>Apache (.htaccess)</summary>
544
+
545
+ ```apache
546
+ <IfModule mod_headers.c>
547
+ Header always set Cross-Origin-Opener-Policy "same-origin"
548
+ Header always set Cross-Origin-Embedder-Policy "require-corp"
549
+ </IfModule>
550
+
551
+ AddType application/wasm .wasm
552
+ ```
553
+ </details>
554
+
555
+ <details>
556
+ <summary>Vite (development)</summary>
557
+
558
+ ```typescript
559
+ export default defineConfig({
560
+ server: {
561
+ headers: {
562
+ 'Cross-Origin-Opener-Policy': 'same-origin',
563
+ 'Cross-Origin-Embedder-Policy': 'credentialless',
564
+ },
565
+ },
566
+ });
567
+ ```
568
+ </details>
569
+
570
+ ---
571
+
572
+ ## Configuration
573
+
574
+ ### SDK Initialization
575
+
576
+ ```typescript
577
+ await RunAnywhere.initialize({
578
+ environment: 'development', // 'development' | 'staging' | 'production'
579
+ debug: true, // Enable verbose logging
580
+ });
581
+ ```
582
+
583
+ ### Logging
584
+
585
+ The SDK uses `SDKLogger` for all internal logging. Configure log level and enable/disable:
586
+
587
+ ```typescript
588
+ import { SDKLogger, LogLevel } from '@runanywhere/web';
589
+
590
+ SDKLogger.level = LogLevel.Debug; // Trace | Debug | Info | Warning | Error | Fatal
591
+ SDKLogger.enabled = true; // Toggle all SDK logging
592
+ ```
593
+
594
+ ### Events
595
+
596
+ Subscribe to SDK lifecycle events:
597
+
598
+ ```typescript
599
+ import { EventBus } from '@runanywhere/web';
600
+
601
+ EventBus.shared.on('model.downloadProgress', (event) => {
602
+ console.log(`Download: ${(event.data.progress * 100).toFixed(0)}%`);
603
+ });
604
+
605
+ EventBus.shared.on('model.loadCompleted', (event) => {
606
+ console.log(`Model loaded: ${event.data.modelId}`);
607
+ });
608
+ ```
609
+
610
+ ---
611
+
612
+ ## Error Handling
613
+
614
+ The SDK uses typed errors with error codes:
615
+
616
+ ```typescript
617
+ import { SDKError, SDKErrorCode } from '@runanywhere/web';
618
+
619
+ try {
620
+ await TextGeneration.generate('Hello');
621
+ } catch (err) {
622
+ if (err instanceof SDKError) {
623
+ switch (err.code) {
624
+ case SDKErrorCode.NotInitialized:
625
+ console.error('SDK not initialized');
626
+ break;
627
+ case SDKErrorCode.ModelNotLoaded:
628
+ console.error('No model loaded');
629
+ break;
630
+ default:
631
+ console.error(`SDK error [${err.code}]: ${err.message}`);
632
+ }
633
+ }
634
+ }
635
+ ```
636
+
637
+ ---
638
+
639
+ ## Demo App
640
+
641
+ A full-featured example application is included at `examples/web/RunAnywhereAI/`. It demonstrates all SDK capabilities across seven tabs: Chat, Vision, Voice, Transcribe, Speak, Storage, and Settings.
642
+
643
+ ```bash
644
+ cd examples/web/RunAnywhereAI
645
+ npm install
646
+ npm run dev
647
+ ```
648
+
649
+ The demo app runs on Vite with Cross-Origin Isolation headers pre-configured.
650
+
651
+ ---
652
+
653
+ ## npm Package
654
+
655
+ ```
656
+ @runanywhere/web
657
+ ```
658
+
659
+ ### Published Exports
660
+
661
+ | Export | Description |
662
+ |--------|-------------|
663
+ | `RunAnywhere` | SDK lifecycle (initialize, shutdown, capabilities) |
664
+ | `TextGeneration` | LLM text generation and streaming |
665
+ | `STT` | Speech-to-text transcription |
666
+ | `TTS` | Text-to-speech synthesis |
667
+ | `VAD` | Voice activity detection |
668
+ | `VLM` | Vision-language model inference |
669
+ | `VoicePipeline` | STT -> LLM -> TTS orchestration |
670
+ | `VoiceAgent` | Complete voice agent with C API pipeline |
671
+ | `ToolCalling` | Function calling with typed tool definitions |
672
+ | `StructuredOutput` | JSON schema-guided generation |
673
+ | `Embeddings` | Vector embedding generation |
674
+ | `Diffusion` | Image generation (WebGPU, scaffold) |
675
+ | `AudioCapture` | Microphone capture via Web Audio API |
676
+ | `AudioPlayback` | Audio playback via Web Audio API |
677
+ | `VideoCapture` | Camera capture and frame extraction |
678
+ | `ModelManager` | Advanced model download/storage/loading |
679
+ | `OPFSStorage` | Low-level OPFS persistence |
680
+ | `VLMWorkerBridge` | Web Worker bridge for VLM inference |
681
+ | `SDKLogger` | Structured logging |
682
+ | `SDKError` | Typed error hierarchy |
683
+ | `EventBus` | SDK event system |
684
+ | `detectCapabilities` | Browser feature detection |
685
+
686
+ ---
687
+
688
+ ## FAQ
689
+
690
+ ### Does this work offline?
691
+
692
+ Yes. Once models are downloaded and cached in OPFS, the SDK works entirely offline. No server, API key, or network connection is needed for inference.
693
+
694
+ ### Where are models stored?
695
+
696
+ Models are stored in the browser's Origin Private File System (OPFS), a sandboxed persistent storage API. Files persist across browser sessions but are origin-scoped and not accessible via the regular file system. If OPFS quota is exceeded, the SDK falls back to an in-memory cache for the current session.
697
+
698
+ ### How large are the WASM files?
699
+
700
+ The core `racommons.wasm` is approximately 3.6 MB (all backends). The sherpa-onnx WASM (for TTS/VAD) is approximately 12 MB and is loaded separately only when needed. These are downloaded once and cached by the browser.
701
+
702
+ ### Is my data private?
703
+
704
+ Yes. All inference runs entirely in the browser via WebAssembly. No data is sent to any server. Audio, text, and images never leave the device.
705
+
706
+ ### Which browsers are supported?
707
+
708
+ Chrome 96+ and Edge 96+ are fully supported. Firefox 119+ works but lacks WebGPU. Safari 17+ has basic support but limited OPFS reliability. Mobile browsers have memory constraints that limit larger models.
709
+
710
+ ### Can I use a custom model?
711
+
712
+ Yes. Any GGUF-format model compatible with llama.cpp works for LLM/VLM. STT models use ONNX format via whisper.cpp or sherpa-onnx. TTS models use Piper ONNX format.
713
+
714
+ ---
715
+
716
+ ## Troubleshooting
717
+
718
+ ### "SharedArrayBuffer is not defined"
719
+
720
+ **Cause:** Missing Cross-Origin Isolation headers.
721
+
722
+ **Fix:** Add the required headers to your server configuration. See [Cross-Origin Isolation Headers](#cross-origin-isolation-headers). The SDK will fall back to single-threaded mode if headers are missing.
723
+
724
+ ### "Model failed to load"
725
+
726
+ **Cause:** CORS error, wrong file path, or corrupted download.
727
+
728
+ **Fix:** Ensure the model URL has proper CORS headers or serve from the same origin. Check the browser console for network errors. Try deleting the model from OPFS storage and re-downloading.
729
+
730
+ ### "Out of memory" / tab crashes
731
+
732
+ **Cause:** Model too large for available browser memory.
733
+
734
+ **Fix:** Use smaller quantized models (Q4_0 instead of Q8_0). Close other browser tabs. On mobile, models larger than 1 GB may exceed available memory.
735
+
736
+ ### VLM inference is slow
737
+
738
+ **Cause:** CLIP image encoding is computationally expensive in WASM.
739
+
740
+ **Fix:** Use smaller capture dimensions (256x256 is recommended). The VLM runs in a dedicated Web Worker so the UI remains responsive during inference.
741
+
742
+ ### OPFS storage not persisting
743
+
744
+ **Cause:** Browser may evict storage under memory pressure, or Incognito mode.
745
+
746
+ **Fix:** The SDK requests persistent storage automatically. Ensure you are not in Incognito/Private mode. Safari has known OPFS reliability issues.
747
+
748
+ ---
749
+
750
+ ## Known Limitations (Beta)
751
+
752
+ - No test suite yet (planned for v0.2.0)
753
+ - No model hash verification on download
754
+ - WASM memory allocations in some extension methods lack guaranteed cleanup via `finally` blocks (low probability, planned fix)
755
+ - VLM inference is single-threaded (one frame at a time)
756
+ - No streaming TTS (audio returns all-at-once)
757
+ - Safari OPFS support is unreliable
758
+ - Mobile browsers have limited memory for large models
759
+
760
+ ---
761
+
762
+ ## Contributing
763
+
764
+ See the repository [Contributing Guide](../../CONTRIBUTING.md) for details.
765
+
766
+ ```bash
767
+ # Clone and set up
768
+ git clone https://github.com/RunanywhereAI/runanywhere-sdks.git
769
+ cd runanywhere-sdks/sdk/runanywhere-web
770
+
771
+ # Install dependencies
772
+ npm install
773
+
774
+ # Build TypeScript
775
+ npm run build:ts
776
+
777
+ # Run the demo app
778
+ cd ../../examples/web/RunAnywhereAI
779
+ npm install
780
+ npm run dev
781
+ ```
782
+
783
+ ---
784
+
785
+ ## Support
786
+
787
+ - **Discord:** [Join our community](https://discord.gg/N359FBbDVd)
788
+ - **GitHub Issues:** [Report bugs or request features](https://github.com/RunanywhereAI/runanywhere-sdks/issues)
789
+ - **Email:** founders@runanywhere.ai
790
+
791
+ ---
792
+
793
+ ## License
794
+
795
+ Apache 2.0 -- see [LICENSE](../../LICENSE) for details.