拓冰建站拓冰建站
首页 / 资讯中心 / 正文

WrenAI 浏览器端分析引擎 wren-core-wasm 完全指南:基于 DataFusion 的 WASM 语义层与 SQL 执行

WrenAI 浏览器端分析引擎 wren-core-wasm 完全指南基于 DataFusion 的 WASM 语义层与 SQL 执行【免费下载链接】WrenAIGenBI (Generative BI) for AI agents, an open-source, governed text-to-SQL through an open context layer that turns natural-language questions into trusted dashboards, charts, and SQL across 20 data sources, such as BigQuery, Snowflake, PostgreSQL, ClickHouse, Amazon Redshift, Databricks and more.项目地址: https://gitcode.com/GitHub_Trending/wr/WrenAIwren-core-wasm 是 WrenAI 项目中一个将 Wren Engine 编译为 WebAssembly 的独立 crate让浏览器可以在纯客户端环境中通过 MDLModeling Definition Language语义层直接对 Parquet / CSV / JSON 数据执行 SQL 查询底层由 DataFusion 提供查询能力。本文以 core/wren-core-wasm/.claude/CLAUDE.md 为核心骨架结合 src/lib.rs、TypeScript SDK、justfile 与集成测试 等仓库源码系统讲解该模块的架构设计、WASM 专属约束、MDL 加载模式、API 使用方式与构建测试流程读完即可上手把语义层分析能力嵌入自己的浏览器应用。一、wren-core-wasm 是什么wren-core-wasm 是 Wren Engine 的 WebAssembly 版本用于浏览器原生分析browser-native analytics。它的工作模式可以概括为在浏览器端直接执行 SQL 查询数据来源于 Parquet / CSV / JSON 文件查询路径要经过 MDL 语义层且整个执行过程完全发生在客户端不需要任何服务端计算。浏览器 JS ├── registerParquet(name, bytes) → Arrow RecordBatch → MemTable ├── registerJson(name, json) → Arrow JSON reader → MemTable ├── loadMDL(mdl_json, source) → AnalyzedWrenMDL 表解析 │ URL 模式 sourcehttps://... → ListingTable走 HttpStore │ 本地模式 source./... → 期望预先注册好的表 │ 回退模式 source → 从 tableReference 自动检测 └── query(sql) → MDL 重写 → DataFusion 执行 → JSON 结果从源码结构看该模块的核心价值在于把 WrenAI 的语义层能力MDL 建模、cube 查询从服务端搬进了浏览器为静态站点 本地数据文件形态的分析应用例如 examples 下的演示页提供了完整的执行链路。二、为什么独立成 crateCLAUDE.md 明确指出wren-core-wasm 之所以独立于wren-core/workspace主要基于两个原因使用上游 DataFusioncrates.io 上的 v53而非 Canner fork。WASM 版本直接通过 DataFusion 执行查询不需要 SQL unparser 或方言转译dialect transpilation因此不需要 Canner fork 中针对 unparser 的修复。避免依赖冲突该 crate 被放在wren-core/workspace 之外防止两套 DataFusion 依赖树相互干扰。在 Cargo.toml 中可以验证这一点——datafusion { version 53, default-features false, ... }注释明确写着 DataFusion: upstream latest (NOT Canner fork)。而 WrenAI 仓库根目录下的wren-core/core则使用 Canner fork 版本两者用途不同native 端需要生成目标方言 SQLWASM 端则把 DataFusion 作为最终执行引擎。同时它复用了两个共享代码库wren-core-base../wren-core-base共享的 manifest 类型定义不依赖 DataFusionwren-core语义层../wren-core/core提供 MDL 分析规则例如AnalyzedWrenMDL::analyze_with_tables与apply_wren_on_ctx。三、核心源码结构与 WrenEngine 生命周期CLAUDE.md 指出 crate 是单文件 crate所有核心逻辑集中在 src/lib.rs。WrenEngine是暴露给 JS 的唯一门面结构体内部持有三个关键成员见 lib.rs#L39-L52#[wasm_bindgen] pub struct WrenEngine { ctx: datafusion::execution::context::SessionContext, analyzed_mdl: Optionstd::sync::Arcwren_core::mdl::AnalyzedWrenMDL, runtime: tokio::runtime::Runtime, // 单线程 tokio 运行时 }ctxDataFusion 的会话上下文负责 SQL 解析、规划与执行analyzed_mdlloadMDL之后保留的分析结果供cubeQuery/listCubes读取 manifestruntime单线程 tokio 运行时这是 WASM 环境下的关键设计详见本文WASM 特定约束一节。整个 API 通过#[wasm_bindgen(js_name camelCase)]映射为 JS 侧的 camelCase 方法名。接下来按生命周期逐一讲解。3.1 初始化WrenEngine.new()→ SessionContextnew()构造器lib.rs#L62-L89做了三件事调用console_error_panic_hook::set_once()把 Rust panic 消息输出到浏览器 console避免只看到模糊的RuntimeError: unreachable创建SessionConfig::new().with_target_partitions(1)——强制单分区适配 WASM 单线程环境把会话时区设置为 UTC00:00保证浏览器端的时间戳推断与比较和 native 侧create_wren_ctx行为一致构建tokio::runtime::Builder::new_current_thread()单线程运行时。这里有一个容易被忽视的实现细节为什么 WASM 里还要一个 tokio 运行时因为 DataFusion 的物理算子例如CoalescePartitionsExec它会包裹任何多分区计划如UNION ALL/INTERSECT/EXCEPT内部会调用tokio::task::spawn。如果spawn不在一个活着的 tokio 运行时上下文中执行就会 panic 报there is no reactor running。wasm-bindgen-futures本身并不提供 tokio 调度器上下文所以WrenEngine自己持有一个 current-thread runtime并在查询时用runtime.block_on(...)驱动 future。集成测试 test_union_all_does_not_trap 正是为这个问题的回归而写。3.2 数据注册registerJson / registerParquet / registerCsv在本地模式下物理表需要先注册进引擎之后才能被loadMDL引用。三个注册方法都遵循同一个模式把输入数据读成 Arrow RecordBatch → 包成 DataFusion MemTable → 注册到 SessionContext。registerJson(table_name, json_data)lib.rs#L99-L140输入是 JSON 对象数组字符串如[{a:1,b:x},...]内部先把 JSON 数组转成 NDJSON每行一个对象因为 Arrow 的 JSON reader 只接受 NDJSON 格式用infer_json_schema推断 schema再按 8192 的 batch size 读取为 RecordBatch空数据会返回No data in JSON input错误。registerParquet(table_name, data)lib.rs#L146-L177输入是 Parquet 文件字节JS 侧传Uint8Array/ArrayBuffer使用ParquetRecordBatchReaderBuilder读取支持 snappy 与 lz4 压缩无 zstd原因见依赖一节空文件返回No data in Parquet file错误。registerCsv(table_name, data, options_json)lib.rs#L209-L280这是三者中最灵活的支持通过 JSON 选项字符串定制读取行为完整选项如下Rust 侧定义于 lib.rs#L786-L809选项类型默认值说明headerbooleantrue首行是否为表头delimiterstring,字段分隔符取字符串首字节必须为 ASCIIquotestring引号字符取首字节escapestring未设置转义字符terminatorstring\n或\r\n记录终止符batchSizenumber8192RecordBatch 大小inferRowsnumber1000schema 推断时读取的行数显式提供schema时忽略schemaarray无显式列定义[{name, type, nullable}]提供后跳过推断显式schema支持的类型大小写不敏感包括int8~int64、uint8~uint64、float32/float64、boolean、string/utf8/varchar/text、date/date32/date64、timestamp/timestamp_s/timestamp_ms/timestamp_us/timestamp_ns以及int/integer/bigint/long/float/double/real/number/bool等别名对应 Rust 侧的 arrow_schema_from_columns。单字符选项delimiter/quote 等只取字符串第一个字节非 ASCII 会报single ASCII character错误——测试 rejects non-ASCII delimiter 覆盖了这一行为。3.3 loadMDL三种物理表解析模式loadMDL(mdl_json, source)lib.rs#L307-L344是整个语义层的入口。它先解析 MDL JSON 为Manifest然后根据source参数选择三种模式之一来注册物理表并分析语义层最后调用apply_wren_on_ctxMode::LocalRuntime直接 DataFusion 执行不做 SQL 生成把 MDL 分析规则挂到 SessionContext 上并把分析结果保存在self.analyzed_mdl。三种模式source参数判定逻辑见 is_url_sourceURL 模式source 以http://或https://开头lib.rs#L355-L426为每个 model 注册一个 DataFusionListingTableURL 固定为{source}/{裸表名}.parquet无需预先注册表每个唯一 origin 注册一个 HTTP object storeHttpBuilderDataFusion 通过 HTTP Range 请求读取远端 Parquetschema 通过 Range GET 读取 Parquet footer 推断见 build_listing_table阶段性限制s3://和gs://属于 Phase 4 计划当前会落入本地模式并在缺少表时报错Phase 2 假设扁平的 Parquet 布局若两个不同 schema 下的 model 共享同一个裸表名如raw.orders与staging.orders会静默冲突到{source}/orders.parquet。更丰富的 schema 映射{source}/{schema}/{name}.parquet也在 Phase 4 计划中采用先全部暂存、后统一注册的策略所有 model 的 schema 推断都成功后才会修改self.ctx失败的loadMDL不会留下半注册的表便于重试。本地模式其他非空字符串lib.rs#L431-L475期望调用方已通过registerParquet/registerJson/registerCsv预先注册每个 model 的物理表按裸表名在datafusion.publiccatalog 中查找若任何 model 的表缺失loadMDL会立即返回Unresolved models: [...]错误而不是把问题推迟到查询时——测试 rejects MDL with missing tables in local mode 验证了这一前置校验。回退模式source 为空字符串lib.rs#L480-L552M3 的向后兼容行为自动检测 MDL 中每个 model 的tableReference是否为 URL只要有一个可解析的 URL 就走analyze_with_url_tablesURL 表路径否则走本地表路径保留它是因为历史 MDL 会把 URL 直接嵌在tableReference里。加载完成后裸 model 名会在 MDL 的 catalog/schema通常是wren.public下解析查询时不需要带 catalog 前缀直接用Orders即可。这一点在 test_bare_model_name_query 与 SDK 测试 loads MDL and queries via model name 中均有覆盖。3.4 查询query / cubeQuery / listCubesquery(sql)lib.rs#L599-L633是核心执行路径用self.runtime.block_on(...)包裹保证 DataFusion 内部的tokio::task::spawn有活着的调度器ctx.sql(sql)解析并应用 MDL 分析规则df.collect()执行并收集 RecordBatch用 Arrow JSON writerwith_explicit_nulls(true)显式输出 null把结果序列化为 JSON 对象数组字符串如[{count:42,avg:3.14},...]。cubeQuery(cube_query_json)lib.rs#L643-L660接受 JSON 编码的CubeQuery经由 wren-core 的cube_query_to_sql转成 SQL再委托给query()执行——也就是结构化 cube 查询与手写 SQL 走同一条执行链路。它要求先调用loadMDL否则报No MDL loaded. Call loadMDL() first.。listCubes()lib.rs#L667-L705返回加载的 MDL 中定义的 cube 列表每条记录包含name、baseObject、measures、dimensions、timeDimensions、hierarchies同样要求先loadMDL。这对于 Agent 在调用cubeQuery之前探查有哪些可查询的 cube非常有用。四、TypeScript SDK 封装层raw wasm-bindgen API 暴露的是底层方法真正的开发体验由 sdk/src/index.ts 提供——它把整个 wasm 接口封装成一个类WrenEngine并处理了 WASM 二进制加载、BufferSource 归一化、JSON 序列化/反序列化等细节。初始化与生命周期WrenEngine.init(options?)index.ts#L172-L180加载 WASM 二进制并创建引擎。options.wasmUrl可以是 URL 字符串、URL 对象浏览器内 fetch或BufferSource如 ArrayBuffer、Node.js Buffer直接实例化缺省时通过import.meta.url解析同目录的wren_core_wasm_bg.wasmengine.free()index.ts#L302-L304释放 WASM 内存。数据注册index.ts#L206-L262registerParquet(name, data)接受任意BufferSource对 TypedArray 会保留byteOffset/byteLength视图信息registerJson(name, data)接受 JS 对象数组内部JSON.stringify后传给底层registerCsv(name, data, options?)data可以是 CSV 字符串按 UTF-8 编码或任意BufferSourceoptions即上文表格中的CsvReadOptions。语义层与查询loadMDL(mdl, profile)index.ts#L194-L197mdl是 MDL manifest 对象profile为{ source: string }。注释明确区分三种 source 语义URL 模式、本地模式source: ./data/与回退模式source: 并提醒 URL 模式的裸表名冲突风险query(sql)index.ts#L268-L272返回解析后的Recordstring, unknown[]数组而不是原始 JSON 字符串cubeQuery(query)index.ts#L283-L287入参CubeQueryInput包含cube、measures、dimensions、timeDimensions、filters、limit、offset等字段SDK 注释建议聚合查询优先用它因为 cube 层会自动拼装GROUP BY、DATE_TRUNC和WHERE子句listCubes()index.ts#L295-L299返回强类型的CubeInfo[]。SDK 还导出了一系列 TypeScript 类型定义WrenProfile、Granularityyear 到 minute、FilterOperatoreq/neq/in/not_in/gt/gte/lt/lte/contains/starts_with/is_null/is_not_null、FilterValue、TimeDimensionInput、CubeFilterInput、CubeQueryInput、CubeInfo、CsvReadOptions等与 wren_core_wasm.d.ts手工维护的 wasm-bindgen 输出类型存根共同构成完整的类型层。五、依赖矩阵与版本选择Cargo.toml 的依赖设计直接服务于 WASM 目标每一行都有明确理由依赖版本/特性设计意图DataFusionv53upstreamdefault-features false 选定特性查询引擎不启用 parquet 特性DataFusion 的 parquet 默认开启 zstd无法编译到 WASM改为自己读 Parquet 字节 → RecordBatch → MemTableArrowv58.1jsoncsv特性JSON/CSV 读取器与 JSON 结果写出Parquetv58.1仅snaplz4无 zstd依赖 C 库zstd-sys无法编译到 WASMsnappy/lz4 为纯 Rust 实现object_storev0.13.1awshttp特性URL 模式下 HttpStore 读取远端 Parquetwren-corepath../wren-core/coredefault-features false语义层MDL 分析规则WASM 下不启用多线程wren-core-basepath../wren-core-base共享 manifest 类型无 DataFusion 依赖wasm-bindgen / js-sys / web-sys—WASM ↔ JS 绑定与 console 输出tokiortmacros无rt-multi-thread单线程运行时见 3.1 节chronowasmbind特性WASM 下用js_sys::Date替代SystemTimegetrandom0.2/0.3/0.4 三个主版本并存依赖树中三版都需要显式启用js/wasm_js特性[target.cfg(target_arch wasm32).dependencies]节数据融合方向值得注意DataFusion 的表达式特性nested_expressions、crypto_expressions、datetime_expressions、encoding_expressions、regex_expressions、unicode_expressions也被显式选中意味着浏览器端同样能使用这些函数族。六、WASM 特定约束与性能目标这是该模块区别于 native 端最重要的部分CLAUDE.md 用专门一节列出单线程SessionConfig::with_target_partitions(1)tokio 只用rt无rt-multi-thread。所有物理算子都在单分区、单线程内执行无 zstdzstd-sysC 库编译不到 wasm32 目标因此 Parquet 只支持 snappy lz4纯 Rust 压缩实现无 SystemTimechrono 必须启用wasmbind特性时间相关操作走js_sys::Dategetrandom依赖树中 0.2、0.3、0.4 三个主版本同时存在每个都需要在 wasm32 目标下显式指定 JS 后端js/wasm_js特性macOS 构建C 依赖需要 LLVMbrew install llvmjustfile 会自动设置CC_wasm32_unknown_unknown、AR_wasm32_unknown_unknown等环境变量指向 Homebrew 的 LLVM二进制体积约 68 MB raw / 约 14 MB gzipgzip 目标是保持在 15 MB 以下。just size命令可以直接报告当前构建产物的 raw 与 gzip 体积。七、开发命令与构建流程CLAUDE.md 列出了完整的 just 命令集justfile 有对应实现just build # 完整构建WASMrelease TypeScript SDK → dist/ just build-wasm # 仅 WASMwasm-pack → pkg/macOS 自动检测 LLVM just build-wasm-dev # WASM debug 构建更快不加 --release just build-dist # 从 pkg/ TS 组装 dist/要求 pkg/ 已存在 just test # SDK 集成测试要求 dist/ just typecheck # 仅 TypeScript 类型检查 just serve # 本地 HTTP 服务localhost:8787用于浏览器示例 just size # 报告 WASM 二进制体积raw gzip just clean # 清理 pkg/、dist/、target/各命令的职责链build由build-wasmwasm-pack build --target web --release与build-distnpm installnode scripts/build.mjs串联而成。其中 scripts/build.mjs 负责把pkg/下的 wasm 产物wren_core_wasm.js、wren_core_wasm.d.ts、wren_core_wasm_bg.wasm、wren_core_wasm_bg.wasm.d.ts拷贝到dist/再调用tsc -p sdk/tsconfig.json编译 TypeScript。just size内部用statgzip -c统计原始与压缩后体积。clean通过rm -rf pkg/ dist/ target/清理三类构建产物。八、npm 包与浏览器示例package.json 定义了wrenai/wren-core-wasm这个 ESM 包入口dist/index.js类型dist/index.d.tsexports同时暴露.与./dist/*files只包含dist/即发布物 ESM 类型 .wasm二进制构建脚本与 justfile 对应build:wasmwasm-pack release、build:ts、build:dist、build、typecheck、testnode --test sdk/tests/index.test.mjsengines.node 16sideEffects: falseTypeScript 为唯一 devDependency发布后可经由 CDNunpkg/jsDelivr直接使用。仓库提供了 6 个浏览器示例页examples/其中两个最能体现核心能力inline.htmlexamples/inline.html演示完整的最小链路init()加载 WASM →registerJson(orders, ...)注册内嵌 JSON →loadMDL(mdl, )加载语义层 → 执行 SQLSELECT customer, sum(amount) AS total, count(*) AS orders FROM Orders GROUP BY customer ORDER BY total DESC并把结果渲染成表格。url-mode.htmlexamples/url-mode.html演示 URL 模式把source指向{origin}/examples/data/loadMDL自动注册 ListingTableDataFusion 通过 HTTP Range 请求远端 Parquet全程不需要registerParquet。运行这些示例用 examples/serve.mjsnode examples/serve.mjs [port]默认 8787。这个静态服务器特意实现了CORS Range 请求 MIME 类型URL 模式下 DataFusion 读取远端 Parquet 依赖 HTTP Range 请求读取 footer 推断 schema因此服务器必须响应206 Partial Content并暴露Accept-Ranges: bytes、Content-Range等响应头同时用路径规范化拦截目录穿越。九、测试与代码约定测试体系分两层Rust WASM 测试使用wasm-bindgen-testlib.rs 测试模块配置为run_in_node_experimental可在 Node 中跑无需浏览器。代表性用例test_basic_query注册 JSON 后执行count(*)/avg(amount)聚合test_union_all_does_not_trap回归测试——0.4.0 在UNION ALL这类多分区计划上会因tokio::spawn无 reactor 而RuntimeError: unreachabletest_is_url_source与test_extract_bare_table_name覆盖 URL 判定s3://、gs://当前不算 URL 模式与tableReference解析引号内的点不会被错误切开如schema.has.dot→has.dottest_bare_model_name_query验证裸 model 名无需wren.public.前缀即可查询。SDK 集成测试sdk/tests/index.test.mjs使用 Node 内置node:test加载真实dist/wren_core_wasm_bg.wasm验证全链路。覆盖了引擎多实例隔离、JSON/CSV/Parquet 注册、空结果集、类型保持、聚合、MDL 加载本地/回退模式、缺失表报错、非法 SQL/表报错、集合算子UNION/INTERSECT/EXCEPT、free()、cubeQuery/listCubes及时间维度分桶dateRangegranularity: month产生created_at__month列等场景。代码约定来自 CLAUDE.md 的 Conventions 节Rust 使用cargo fmt格式化、clippy -D warnings零警告 lintTypeScript 使用 strict 模式、ES2020 target面向 JS 的 API 用#[wasm_bindgen(js_name camelCase)]命名错误以JsError传播浏览器 console 中可见含堆栈的错误信息测试Rust 侧wasm-bindgen-testSDK 侧node:test。十、小结什么时候适合使用 wren-core-wasm结合以上源码证据wren-core-wasm 的适用形态可以归纳为数据以 Parquet/CSV/JSON 文件形式存在、且可以通过 HTTP Range 或本地上传提供给浏览器的纯前端分析场景。它的语义层能力MDL 建模、cube 查询、model 名解析全部在客户端完成服务端只承担静态文件服务。URL 模式直接读取远端 Parquet 省去了数据落地环节本地模式则适合文件由用户本地选择的场景。需要留意的边界均可在 src/lib.rs 中确认单线程执行意味着计算密集型聚合的性能上限受限于单个浏览器线程s3:///gs://支持与 schema 级目录映射属于 Phase 4 计划URL 模式下同名裸表存在文件级静默冲突风险。若你的数据源是 BigQuery、Snowflake、PostgreSQL 这类需要通过连接器访问的数据库则应回到 WrenAI 的服务端语义层方案而不是浏览器端 WASM 引擎。进一步阅读WrenAI 的 MDL 概念可参考 docs/core/concepts/what_is_mdl.mdWASM 语义层的 manifest 类型定义位于 wren-core-baseMDL 分析规则的语义层实现位于 wren-core/core/src/mdl。【免费下载链接】WrenAIGenBI (Generative BI) for AI agents, an open-source, governed text-to-SQL through an open context layer that turns natural-language questions into trusted dashboards, charts, and SQL across 20 data sources, such as BigQuery, Snowflake, PostgreSQL, ClickHouse, Amazon Redshift, Databricks and more.项目地址: https://gitcode.com/GitHub_Trending/wr/WrenAI创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门