- AI
- 模型推理服务
- 推理引擎
- 本地部署
- 多模态
【免费下载链接】runanywhere-sdks
Production ready toolkit to run AI locally
本指南以 bindings/web/example 仓库中的最小示例应用为骨架,完整讲解 RunAnywhere Web SDK 在浏览器中的真实启动流程、模型注册与流式生成调用链,以及跨源隔离、WASM 构建等关键工程细节。读完本指南,你将掌握如何在纯 DOM + Vite 项目中正确初始化 SDK 双阶段启动、注册 llama.cpp 后端、让模型按需下载加载,并通过generateStream消费流式事件,同时理解该应用作为仓库内贡献者验证环境(contributor harness)与 Playwright 浏览器测试默认目标的设计原理。
一、这个最小示例是什么
bindings/web/example是整个仓库中"最小的、能证明 Web SDK 可用"的应用:一个输入框、一个 Generate 按钮、一段流式输出的回答。它不依赖任何前端框架,直接使用原生 DOM 操作,全部逻辑集中在 src/main.ts 与一个 index.html 中。
它在项目里扮演两个角色:
- 仓库内贡献者验证环境(contributor harness):默认构建时通过 Vite 别名(alias)与
tsconfig.json的 paths 映射,把@runanywhere/web、@runanywhere/web-llamacpp和@runanywhere/proto-ts直接指向仓库源码目录。这意味着修改 SDK 源码后无需发布即可在此应用里立即看到效果。 - 浏览器测试默认目标:
bindings/web/tests/browser/*下的 Playwright 测试默认驱动该应用;RA_E2E_APP_DIR环境变量可以将同一批测试指向其他应用(如发布验收用的 release harness)。
从源码结构看,整个应用刻意保持"零框架、单屏":index.html里只有一个status段落、一个prompt文本域、一个generate按钮和一个output预格式化块。这是为了让任何人(人类或自动化测试)都能在 10 秒内看懂它做了什么。
二、运行与构建命令
在bindings/web/example目录下,核心命令如下:
npm install npm run typecheck # 类型检查 npm run build # 生产构建,产物输出到 dist/ npm run preview # 预览 dist/,地址 http://localhost:3000 npm run dev # 开发服务器,地址 http://localhost:3000这些脚本定义在 package.json 中:
dev/preview:vite --host localhost --port 3000 --strictPort,端口固定 3000,端口被占用会直接报错而非换端口。typecheck:tsc --noEmit,基于 tsconfig.json。typecheck:installed:tsc --noEmit -p tsconfig.installed-sdk.json,用于针对"已安装的 SDK 包"做类型检查(见下文 RAC_USE_INSTALLED_SDK)。
构建前置条件:四个 canonical WASM 对必须存在。npm run build依赖四个标准 Emscripten 运行时产物对(每个包含.js与.wasm两个文件):
| baseName | 所属包 | 作用 |
|---|---|---|
racommons | core | SDK 核心 commons WASM |
racommons-llamacpp | llamacpp | llama.cpp CPU 后端 |
racommons-llamacpp-webgpu | llamacpp | llama.cpp WebGPU 后端 |
racommons-onnx-sherpa | onnx | ONNX / sherpa 语音后端 |
这四个对分别位于bindings/web/packages/*/wasm目录。构建前需先在bindings/web/下执行npm run build:wasm:all生成它们。Vite 插件(vite.config.ts 中的copyWasmPlugin)会在buildStart阶段检查这些文件是否存在且非空,缺失时会直接让构建失败并列出缺失文件名,而不是产出一个只在浏览器里才报错的残缺 bundle——这是刻意的 fail-fast 设计。
切换到已安装的 SDK(发布消费方验证模式)
设置环境变量RAC_USE_INSTALLED_SDK=1后,模块解析与 WASM 源目录都会切换到node_modules中已安装的@runanywhere/*包。这是发布消费方门禁(release consumer gate)使用的模式:安装发布候选 tarball 后,验证真实发布包而不是仓库源码。
RAC_USE_INSTALLED_SDK=1 npm run build RAC_USE_INSTALLED_SDK=1 npm run typecheck:installedtypecheck:installed会清空 tsconfig 中的本地源码 paths 映射(见 tsconfig.installed-sdk.json),确保类型检查针对的是真正安装进node_modules的包,而非源码路径。
三、应用到底做了什么:双阶段启动与流式生成
整个应用逻辑就在 src/main.ts。启动顺序至关重要——它精确镜像了 SDK 文档化的双阶段初始化流程:
await RunAnywhere.initialize({ environment: 'development' }); // 阶段一:加载 racommons.wasm await LlamaCPP.register({ acceleration: 'auto' }); // 阶段二:加载 racommons-llamacpp[-webgpu].wasm await RunAnywhere.completeServicesInitialization(); // 已废弃入口,但此处并非无效: // initialize() 已在后台启动 Phase 2, // 此调用负责 join/await 它 RunAnywhere.models.register({ id: 'smollm2-360m-q8_0', ... }); // 目录(catalog)由应用持有之后是流式生成一次回答:
for await (const event of RunAnywhere.llm.generateStream(prompt, { model: MODEL_ID })) { // event.type: 'textDelta' | 'reasoningDelta' | 'completed' | 'failed' | 'cancelled' }3.1 启动流程逐行拆解
RunAnywhere.initialize({ environment: 'development' }):初始化 SDK 核心,加载racommons.wasm。environment: 'development'会启用开发模式日志与遥测。LlamaCPP.register({ acceleration: 'auto' }):注册 llama.cpp 后端,加载racommons-llamacpp[-webgpu].wasm。acceleration: 'auto'让 SDK 根据当前设备自动选择 CPU 或 WebGPU 路径。RunAnywhere.completeServicesInitialization():文档注释明确标注为 deprecated(已废弃)的转发入口。它保留在这里是为了兼容文档化的两阶段启动写法——实际上initialize()已经折叠了两个阶段,这个调用只是在后台 join/await 已启动的 Phase 2。RunAnywhere.models.register(...):把模型元数据注册进 SDK 的模型注册表。main.ts 中注册的是smollm2-360m-q8_0:
RunAnywhere.models.register({ id: MODEL_ID, // 'smollm2-360m-q8_0' name: 'SmolLM2 360M Q8_0', category: ModelCategory.MODEL_CATEGORY_LANGUAGE, // 语言模型 framework: InferenceFramework.INFERENCE_FRAMEWORK_LLAMA_CPP, format: ModelFormat.MODEL_FORMAT_GGUF, // GGUF 格式 url: 'https://huggingface.co/HuggingFaceTB/SmolLM2-360M-Instruct-GGUF/resolve/main/smollm2-360m-instruct-q8_0.gguf', sizeBytes: 386_404_992, // 约 386 MB memoryRequiredBytes: 500_000_000, // 建议内存 500 MB contextLength: 2048, });为什么应用必须在启动时调用一次models.register?因为浏览器版 SDK不内置模型目录(catalog)——generateStream只能解析注册表里已有的 id。模型下载与加载则由 SDK 负责:在生成请求中命名options.model即可,首次使用时 SDK 会自动下载并加载模型,应用自身从不编排下载/加载流程。
3.2 流式事件消费
generate()函数(main.ts 第 78 行起)调用generateStream并逐个处理事件:
textDelta:增量文本,累加后写入output元素,实现打字机效果;reasoningDelta:推理过程的增量文本(如思维链);completed:生成完成,从event.result读取最终文本、outputTokens(输出 token 数)与tokensPerSecond(生成速度),状态栏显示Done — N tokens at X.X tok/s.;failed:生成失败,读取event.error.message;cancelled:生成被取消。
generateStream的第二个参数还演示了maxOutputTokens: 256的用法,用于限制最大输出 token 数。
四、浏览器环境要求:跨源隔离与 WASM 产物拷贝
4.1 COOP/COEP 跨源隔离
SharedArrayBuffer——以及依赖它的 pthread CPU WASM 构建——要求页面处于跨源隔离(cross-origin isolation)状态。因此 vite.config.ts 对 dev 服务器和 preview 服务器都设置了响应头:
const isolationHeaders = { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'credentialless', } as const;同时生产构建目标被固定为chrome86(Web SDK 文档化的最低浏览器版本),防止未来 Vite 主版本通过其动态的baseline-widely-available默认值静默抬高浏览器要求。
4.2 为什么 WASM 必须以原始文件名拷贝
构建过程会把四个 canonical Emscripten.js/.wasm对以原始文件名拷贝到dist/assets/(copyWasmPlugin的writeBundle钩子):
- Emscripten glue 通过
new URL("x.wasm", import.meta.url)解析自己的二进制文件; - 每个启用 pthread 的模块还会按精确文件名生成 worker——如果只有 Vite 加了 hash 的副本,worker 握手会失败,CPU 构建会永远卡在等待 pthread 池上。
这正是 vite 配置中assetsInclude: ['**/*.wasm']与copyWasmPlugin存在的意义。构建日志会逐对打印拷贝结果,例如✓ Copied racommons-llamacpp-webgpu.wasm (XX.X MB)。
4.3 开发体验细节
vite.config.ts还做了两件事来保证开发体验:
optimizeDeps.exclude: ['@runanywhere/web', '@runanywhere/web-llamacpp']:排除这两个包进入 Vite 依赖预构建,避免重复打包出两个 SDK 单例;- 所有别名必须指向同一份源码模块——如果某个别名解析到了
dist/,就会创建第二个 SDK 单例(duplicate SDK singleton),这是配置注释中特别强调的坑。
五、浏览器测试就绪契约(Readiness Contract)
bindings/web/tests/browser/*下的 Playwright 测试不依赖 DOM 布局来判定应用状态,而是读取 src/readiness.ts 发布的两个全局变量。这保证了即使应用被重写,测试门禁依然有效:
| 全局变量 | 承载内容 |
|---|---|
window.__RUNANYWHERE_AI_READY__ | 启动进度快照(state、backend、step、reason、shellReady) |
window.__RUNANYWHERE_SDK__ | 导入的 SDK 单例,供公共 API 面探测 |
此外,根<html>元素会把后端状态镜像为data-runanywhere-ai-backend属性,Playwright 无需轮询脚本状态即可等待该属性。
ReadinessSnapshot的类型定义(readiness.ts第 28 行起)包含:
ready:应用是否可接受提示词;state:'booting' | 'initializing-sdk' | 'interactive' | 'error';backend:'pending' | 'registered' | 'unavailable';step:细化到'booting' | 'initializing-sdk' | 'registering-llamacpp' | 'registering-catalog' | 'interactive' | 'error';shellReady:单屏 shell 是否可用(smoke 测试读取它);reason:当前状态的人类可读说明;错误时附加error字段。
publishReadiness()每次合并快照补丁并同步更新 DOM 属性;publishSDK()把 SDK 单例挂到window.__RUNANYWHERE_SDK__。main.ts 中的boot()在不同阶段调用publishReadiness({...}),把启动进度逐步上报——从initializing-sdk("Loading the commons WASM.")到registering-llamacpp,再到registering-catalog,最终ready: true进入interactive状态。
运行浏览器测试
从bindings/web/执行:
npm run test:browser:smoke # playwright test tests/browser/backend-readiness.spec.ts tests/browser/hybrid-stt.spec.ts npm run test:browser # 完整默认套件 npm run test:browser:release # RA_RUN_FULL_E2E=1 playwright test tests/browser/release-app.e2e.spec.ts根据 playwright.config.ts 第 59 行,默认appDir = process.env.RA_E2E_APP_DIR ?? resolve(__dirname, 'example')——即默认驱动本示例应用,发布验收测试可把RA_E2E_APP_DIR指向外部 checkout 的 release harness。
六、验证:什么才算"真的跑通了"
构建和类型检查只是冒烟检查。真正的验证是一次完整的浏览器启动:模型下载 → 模型加载 → 流式回答。文档记录的最后一次验证是针对npm run preview完成的:
- COOP/COEP 响应头已生效(跨源隔离开启);
- 四个 canonical WASM 对都以 JavaScript /
application/wasmMIME 类型正常提供; smollm2-360m-q8_0在 WebGPU llama.cpp 路径上完成下载、加载并成功生成。
这也给出了一个可复现的验收清单:启动npm run preview,打开http://localhost:3000,输入提示词点击 Generate,观察状态栏从 "Generating with smollm2-360m-q8_0 (first run downloads the model)…" 推进到 "Done — N tokens at X.X tok/s."——如果模型首次运行会自动下载,说明按需下载/加载链路是通的。
七、总结
bindings/web/example以不到百行代码演示了 RunAnywhere Web SDK 在浏览器端最核心的完整链路:
- 双阶段启动:
initialize()→LlamaCPP.register()→completeServicesInitialization(); - 应用持有目录:通过
models.register注册模型元数据,模型下载/加载交给 SDK 按需完成; - 流式生成:
generateStream的五类事件(textDelta/reasoningDelta/completed/failed/cancelled)驱动 UI 更新; - 工程基建:COOP/COEP 跨源隔离、WASM 原始文件名拷贝、构建失败前置检查、就绪契约全局变量。
对于想要在自己项目中集成 RunAnywhere Web SDK 的开发者,这个示例是最好的起点:复制 index.html 与 src/main.ts 的骨架,替换模型 id 与 UI 元素,即可快速跑通首个浏览器端本地 AI 应用。
- AI
- 模型推理服务
- 推理引擎
- 本地部署
- 多模态
【免费下载链接】runanywhere-sdks
Production ready toolkit to run AI locally
相关推荐
RunAnywhere Web SDK 最小示例应用全解析:浏览器端本地 AI 推理的完整落地路径
RunAnywhere Web SDK 最小示例应用全解析:浏览器端本地 AI 推理的完整落地路径 本篇文章以 bindings/web/example/REA
AI模型推理服务推理引擎本地部署多模态jcode 性能优化技能实战指南:从指标定义到瓶颈归因的完整工作流
jcode 性能优化技能实战指南:从指标定义到瓶颈归因的完整工作流 导读 本篇指南围绕 jcode 仓库内置的 optimization 技能( .jcode/
AI模型推理服务推理引擎本地部署多模态RxDB Quickstart:从零构建一个浏览器端的实时本地优先应用
RxDB Quickstart:从零构建一个浏览器端的实时本地优先应用 导读 本文是 RxDB(local first 数据库,运行于所有 JavaScript
数据库NoSQL嵌入式数据库实时数据库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考