概览与架构
这篇文档解释 @lynx-js/genui/openui 是什么、OpenUI Lang 如何映射成可信的
ReactLynx 组件树,以及一段流式 Agent response 如何变成最终 UI。
这个包是什么
@lynx-js/genui/openui 是面向 OpenUI Lang v0.5 的 ReactLynx 客户端运行时。
它把 @openuidev/lang-core 提供的框架无关 parser/evaluator,与 ReactLynx
renderer 和内置移动端组件 Library 组合在一起。
这个包提供:
- 处理原始或预解析 OpenUI 输入的
<OpenUiRenderer>; createOpenUiLibrary()和 26 个可信组件实现;- 面向模型流式输出的增量解析;
- 响应式
$variables、表达式求值和表单状态; Query()、Mutation()和多步骤Action([...])执行;- 结构化 parser、runtime、render 和 tool errors;
- 用来向 Agent 描述同一组件 contract 的 prompt builders。
它不会托管 Agent、调用 LLM、定义网络传输,也不提供你的后端工具。这些部分由 你的应用负责。
Quick start
在 ReactLynx 应用里安装包,并引入可选的默认主题:
最简单的合法 response 是:
OpenUI 使用位置参数。Parser 按每个组件 Zod schema 的字段顺序,把位置参数映射为 具名 props。它允许 forward references,所以 root 可以引用 response 后面才声明的 statements。
心智模型
在普通 React 中,是你的源码选择组件并传入 props:
在 OpenUI 中,Agent 用声明式 assignments 表达同样的意图:
Renderer 不会执行生成的 JavaScript。它用 Library 生成的 JSON Schema 解析文本,
把合法 component calls 转换为 element tree,再按名称查找对应的可信 ReactLynx
实现。没有注册进 Library 的组件无法被渲染。
所以 Library 是 wire 两侧共享的 contract:
新增或覆盖组件时,必须保持 prompt Library 和 renderer Library 一致。名称或 prop 顺序不一致,会让 Agent 产出 client 无法校验的文本。
一分钟理解 OpenUI Lang v0.5
一段 OpenUI program 每行包含一个 assignment,共有三类 statement:
表达式可以包含 primitives、arrays、objects、references、member access、operators、
ternaries,以及 @Count(...) 等 built-ins。Renderer 必须找到名为 root 的
statement;默认情况下它的组件应为 Stack,因为 Stack 是默认 Library root。
State values 是响应式的。当 $status 改变,读取它的表达式会重新求值,参数依赖
它的 Queries 也会刷新。
完整语言语法和 built-ins 见 OpenUI Lang v0.5 规范。
端到端全貌
OpenUI 是一个由 Agent 编写声明式 UI、client 解析求值并渲染的循环。
- 你的 transport 把用户请求发送给 Agent 服务。
- 服务使用由同一 client component contract 构建的 OpenUI system prompt 调用模型。
- Transport 把 chunks 追加到同一个累计
response字符串,再传给<OpenUiRenderer>。 - Streaming parser 校验已完成的 statements,并解析引用关系。
- Runtime 初始化状态、求值表达式,并通过你的
toolProvider执行完整 Queries。 - Renderer 遍历求值后的 root,挂载 Library 中可信的 ReactLynx 组件。
- 用户交互触发 action steps。宿主 actions 通过
onAction抛出;状态和 tool steps 留在 runtime 内执行。
Client 内部
原始 response 路径会创建完整的 v0.5 runtime:
几个重要的 runtime 行为:
- 增量解析。 累计 response 持续增长时,parser 会缓存已经完成的 statements。 Forward references 在目标到达后变为可渲染状态。
- 稳定的 Library。 用
useMemo或组件外常量只创建一次 Library。改变其对象 identity 会创建新 parser,并可能丢失已有解析工作。 - Streaming guard。 生成期间传入
isStreaming。Query/mutation 会等待输出 稳定,内置交互也保持禁用。 - 响应式状态。
$variables和 form values 位于同一个外部 Store 中。onStateUpdate可以持久化 snapshots;initialState中以$开头的值会 hydrate 响应式 declarations。 - Queries 与 mutations。 Query statement 完整后执行,并在引用状态变化时重新
执行。Mutation 只注册定义,直到 action 调用
@Run(mutationRef)才执行。 - 顺序 actions。
@Run、@Set、@Reset、@ToAssistant和@OpenUrl按顺序执行。Mutation 失败会终止后续 steps。 - 组件软失败。 Element 的 component name 未注册时不会渲染任何内容。其他
parse、evaluation、render 和 tool failures 会在 streaming 稳定后交给
onError。
谁负责什么
<OpenUiRenderer> props
所有新接入都应使用原始 response 形式。
<OpenUiRenderer result={parseResult} library={library}> 仍为 legacy/static
callers 保留。它可以渲染预解析 element tree 并转发简单 actions,但不会创建 v0.5
QueryManager,也无法完整执行 @Run、@Set 和 @Reset。新 v0.5 接入不要使用
这条路径。
Tool providers
本地函数可以通过 name-to-function map 传入:
也可以传入带 callTool({ name, arguments }) 的 MCP-compatible 对象。Runtime 会在
表达式求值前提取结构化 MCP tool result。缺失工具和工具失败会通过 onError
报告。
Exports
组件样式、core renderer stylesheet 和 Material Icons font 都是由对应模块自动引入的
实现细节。不要导入 styles/catalog 或 dist/core 下的私有文件。

