@coveo/thermidor

中文文档
0.9.0发布于 2026/11/11Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 2116

用于搜索和谈话体验的统一、框架不可知头引擎的实验软件包

中文使用文档
文档目录18 节
  1. 这个想法
  2. 公众表面
  3. 会议开幕
  4. 调度行动
  5. 提交提示
  6. 构成部分行动
  7. 一次一个请求
  8. 同步和缓存动作
  9. 乐观价值
  10. 货币冲突
  11. 转动/回转模式
  12. 会话范围表面
  13. 验证和显示值
  14. 注射计划、终点和上下文
  15. 序列化
  16. 记录的临时债务和范围外的项目
  17. 发展
  18. 建筑

库维奥热电站

@coveo/thermidor 是一个实验性,框架不可知的客户端 一个单一,状态,意图的路径 统一反向终点它暴露了一个工厂 createSession(config) ——这个返回 a Session: 一个简单的可观测列表 Turns 从服务器发送事件(SSE)流中折叠出,外加一个单一的动作发送出入口直接接通了渲染器.

包裹是私人的,实验性的,只消耗在这个单体内(由 samples/thermidor/demo-schema-react) (中文(简体) ).

有一个 没有引擎,没有接口层,没有Redux/RTK,没有每个端点外观旧订正中提及这些层面已过时.

这个想法

服务器串流图定义 A2UI (英语) 组件; 客户端生成服务器发送的内容。 瑟米多的工作是将消费者的输入传递到端点,将流出的结果折叠成可观察到的转折列表——它不会在折叠后产生任何用例行为. 合同计划,端点URL,以及环境背景都是由消费者注入的,所以一个单一的软件包构建服务于公共,内部(私人-注册计划)和代理部署.

组件状态旅行 内入 通过 A2-UI 数据模型: 服务器发送 updateDataModel 业务 /state/<id>,而被冻结的渲染器通过A2-UI Data Binding({ path: <JSON Pointer> })与该数据模型相对. 制作者是“哑巴”:他们得到解析状态 props 从不给控制器水分 也不加入快照 A2-UI是唯一支持的状态源. 此内置模型取代了早先的远程控制器 / AG- UI StateSnapshot 设计.

公众表面

import {createSession} from '@coveo/thermidor';
import {ComponentContractsSchema} from '@coveo/thermidor-schema';

const session = createSession({
  organizationId: 'my-org',
  accessToken: 'xx-token',
  agentConfigurationId: '00000000-0000-0000-0000-000000000000', // the configured agent that answers the session
  trackingId: 'my-tracking-id',
  contracts: ComponentContractsSchema, // injected Zod discriminated union (Zod 3 or 4)
  navigatorContextProvider: () => getNavigatorContext(), // required; includes language, country, currency
  commerceContextProvider: () => ({cart: getCart()}),
});

session.subscribe(() => render(session.getSnapshot()));

await session.dispatchAction({name: 'submitPrompt', payload: {prompt: 'show me running shoes'}});

createSession(config) 返回 a Session 完全暴露:

成员目的
contracts本次会话中注入的合同计划(只读)可以验证.
turns仅读可观察列表 Turns从溪来折叠(最初为空).
initialization运行 initialize a. 会议开幕的行动,或 undefined。见 会议开幕。 。 。 .
surfaces会话的每个活表面,按创建顺序排列,由谁运行创建.
a2uiMessages会话的整个渲染器流,为A2-UI v0.9形状. 它只会增长:将每个新消息反馈给一个渲染器一次.
state每个活地表的验证数据模型,由地表id键入.
actionError最后一个失败的组件动作( X)ActionError数字 : message, (中文(简体) ), component, (中文(简体) ), name, (中文(简体) ), surfaceId, (中文(简体) ), sourceComponentId),或 undefined。 。 。 .
request发出的请求,或 undefined 当闲置。 见 一次一个请求。 。 。 .
subscribe(listener)记录每次更改转折列表、初始化、, actionError 或者说 request。 。 。 .
getSnapshot()turns, (中文(简体) ), initialization, (中文(简体) ), surfaces, (中文(简体) ), a2uiMessages, (中文(简体) ), state, (中文(简体) ), request 财务报告和审定财务报表 actionError 作为永恒的 SessionSnapshot,更改时替换.
dispatchAction(message)提示的单个切入点( Q)submitPrompt和组件动作,直接作为渲染器的 onAction。 。 。 .
cancel()停止活动流 .
retry(turnId)在错误时重新提交上个回合的输入:它的快取,或打开它的动作.
serialize()将笔录序列化为已版本 SerializedSession。 。 。 .

subscribe 财务报告和审定财务报表 getSnapshot 不依赖 this,所以它们可以通过引用来传递给任何通过引用来比较快照的商店,比如React's useSyncExternalStore(session.subscribe, session.getSnapshot)。软件包本身取决于任何框架.

混凝土 contracts 类型 createSession 呼叫站点线索未断 Session,因此组件类型、动作名称和有效载荷在呼叫地点保持完全打入(否) string,没有 never,没有 unknown) (中文(简体) ).

会议开幕

1. 会议开幕: createSession 发送一个 initialize 返回后动作一个微任务,所以在同一任务中订阅的听众会看到它开始。 它没有表面或源部件,因为还没有部件。 当它飞行时, session.request 实值 {kind: 'initialize'}它穿过 单一请求规则 和冲突再试,并采用会话编号 从它的运行,像一个提示。 制作人以会话外壳作为普通表面回答,如它的搜索框.

运行记录为 session.initialization (单位:千美元)activities, (中文(简体) ), status, (中文(简体) ), error从不作为转弯。 失败 initialize 未重审 : initialization.status 变成 'error',第一个提示仍然打开会话. cancel() 停止它的任何请求。 恢复的会话从不发送,也没有创建会话 initialize: false,用于测试或用于在渲染时创建会话的服务器.

调度行动

session.dispatchAction 是消费者的单一行动入口。 它接受两种信息.

提交提示

在 A2-UI 构成外输入的即时键, 如集成器的搜索框中, 是 a SubmitPromptAction数字 :

session.dispatchAction({name: 'submitPrompt', payload: {prompt: 'show me running shoes'}});

它打开了一个新的流转方向 记录了转弯时的提示 input.prompt,然后将其输入到反向端点。 它不需要变换组件或主动转弯,因此它也开启了会话的第一个转弯. 当另一个请求在逃时,它会等待它(参见: 一次一个请求),以及 retry(turnId) 重新开出一圈.

构成部分行动

组件动作是标准 A2-UI 客户端到服务器消息( NameA2uiClientMessage被冻结的制造器将手伸到其 onAction 处理器,所以它有线 没有适配器:

<A2UIProvider catalog={catalog} onAction={session.dispatchAction}>{/* … */}</A2UIProvider>

鉴于一个消息, dispatchAction 打开它 userAction,从会话的活出地表取回调度部件的区别性物质,无论由哪个运行产生,对照组件内部注入的合同验证动作有效载荷,并在成功时将动作定位到反向终点.

制作方宣布为打开转盘的动作,如后续芯片或搜索选项,作为对话的新转盘被回答,如提示. 制片人用表面宣布,在AG-UI中 metadata 简况metadata.turnOpeningActions,通过表面的ID),所以消费者宣布无所事事,而转弯会一经hopper取出动作就打开. 这样的行动开启了一个新的接踵而至的转折 input.action 记录它(组件、动作名称、地表代号、源节点和有效载荷),然后转弯成为活转。 转弯时的表面 一直没有接受动作 它不需要主动转弯,所以在外壳上打开的转弯动作打开会话的第一个转弯. 任何其他动作只更新表面:其表面快照用活转来记录,或用 session.initialization 在第一个转弯之前.

没错 火灾和遗忘: 回归的承诺总是解决,而永不拒绝,所以消费者不需要 .catch。一个没有 userAction,没有 sourceComponentId,一个解析为无组件的节点,或者一个内部验证失败被丢弃,只发出一个 dev-exected 警告,而什么也没有发出.

一次一个请求

瑟米多每次发送一个请求, initialize,激励和构成行动相同,但 并行行动 制片人会宣布。 请求中发出的命令,是被扣留的;任何警告,都不被发出来;命令中发出的警告,是无效的。 每个 dispatchAction 承诺一旦结束就会解决.

  • session.request 正在发送的请求( e){kind: 'initialize'}, (中文(简体) ), {kind: 'prompt', turnId} 或者说 {kind: 'action', component, name, surfaceId, sourceComponentId, turnId?},没有 turnId 在第一个回合之前),或 undefined 当闲置。 一个动作的运行计数, 包括后续。 同步读取,或者通过 subscribe,以锁定或淡化接口,并延迟发送,直到会话自由. component 是发送节点的区别者,所以消费者可以按其自己的行动清单,如按部件和行动名称,按与下列单位签订的合同键入的使其表面能够使用的动作清单 ComponentActionNames。 。 。 .
  • cancel() 在飞行中停止请求,但 session.request 等它没有受伤 以后再做一些微小任务 之后就发出来 cancel() 被扣留。 等着 request 改为 undefined,通过 subscribe在发送下一个之前.

同步和缓存动作

生产者可以在回合之外回答一些行动,例如针对突出建议即时产品. 在AG -UI中公布它们与地表 metadata 在宣布开启动作时

"metadata": {
  "concurrentActions": {"<surfaceId>": {"fetchInstantProducts": {"cache": {"ttlMs": 60000}}}}
}

每项同时进行的行动可以包括: cache 政策、答案的寿命;没有答案,答案就得不到重复。 最新宣布的表面胜出,空入则撤销. 渲染器只发送:它没有缓存,没有重试和等待自由会话.

  • 无论飞行中的请求如何,同时发出一个行动,从不设定 session.request即时或 cancel() 不制止它,它将被扣留,直到会议有它的迹象.
  • 只应用从节点发送的最新消息 。 飞行中一个更古老的请求被中止,除非动作被缓存:然后它运行到最后,所以它的答案被缓存,一个带有同一种有效载荷的调度会加入它而不是发送另一个.
  • 在宣布的寿命内再次以相同有效载荷发送的缓存动作,从会话缓存中解答而无所寄出. 缓存保存了最后100个答案,活在记忆中,从未被序列化.
  • 它的答案可能只更新会话已经持有的节点的数据模型;其他任何东西,如新的表面或组件,都会被丢去并附带一个 dev-leven的警告,答案是: metadata 未读。 它的书写范围 session.a2uiMessages 财务报告和审定财务报表 session.state,但是没有运行记录它们:后来录制的写到同一路径的文字代替它们, session.serialize() 离开它们,一个恢复的会话会显示该州最后记录的左跑,所以渲染器再次问起.
  • 它的失败,包括一个被拒绝的请求,是一个虔诚的警告. 没有 session.actionError 更改不运行 .

乐观价值

为了显示一个手势的预期值,直到会话解答为止,显示从手势被发出到其发出承诺决定为止的价值,然后让位于制作者的价值. 一个返回它的渲染器 onAction 处理者的承诺 从自己的派遣手 回来 session.dispatchAction 答应我,等待造物师的指令就够了 热子不依赖于UI框架,因此任何状态机制都有效. 与反应 useOptimistic例如,等待发送的过渡会持续那么长:

const [values, addOptimisticValues] = useOptimistic(producerValues);

startTransition(async () => {
  addOptimisticValues(values.map((v) => (v.value === 'Nike' ? {...v, state: 'selected'} : v)));
  await renderer.dispatchAction({event: {name: 'toggleSelect', context: {value: 'Nike'}}});
});

乐观值让位于制作方的手势一结束,无论结果如何,所以一个失败或被扣留的手势不需要回滚. 热力学从不计算预期值:整数是.

货币冲突

当“网关”仍然忙于处理关于届会的另一个请求时,它拒绝新的请求(409, (中文(简体) ), AGENT:ACTION_IN_FLIGHT) (中文(简体) ). 热电站自动重试最多可达30秒,而一个恢复的冲突却无所表现. 如果它放弃了:

  • a 一个 动作 不打开一转而不改变任何转折,从而释放出乐观的持有 session.actionError (一) 报告情况,直至发出下一个行动或提示;
  • a 一个 打开转弯的动作 套装 session.actionError 轮到他 error,可用 retry(turnId) 这是最后的转折;
  • (单位:千美元) 提示轮到 error,可用 retry(turnId) 而这是最后的转折.

转动/回转模式

每个转一对 input 及其流出 response数字 :

interface Turn {
  id: string;
  input: {prompt?: string; action?: TurnAction}; // the prompt, or the action that opened the turn
  response: TurnResponse;
  status: 'streaming' | 'complete' | 'error';
  error?: string; // present iff status === 'error'
}

interface TurnResponse {
  activities: Activity[]; // raw snapshot log, latest snapshot per messageId, defaults to []
  surfaces: DiscoveredSurface[]; // the live surfaces this turn created
  agent?: {messages: AgentMessage[]; reasoningSteps: ReasoningStep[]}; // present only when the router invoked an agent
}

activities 路由中立,而且始终存在. agent 仅当路由器为转弯而引用代理人时才会出现。 消费者读取 turn.input.prompt, (中文(简体) ), turn.response.surfaces (在哪里画转弯的块)和 turn.response.agent?.messages 编号 : turn.response.agent?.reasoningSteps。 。 。 .

会话范围表面

地表活在会话中,而不是创造它们的转弯. 会话来源于一个命令的活动日志:初始化的活动,然后每个转弯. 水面从它的 createSurface 直至 deleteSurface以运行者为准 createSurface 对于活的id取代了表面, 所以一个生产者薄荷一个新的id 对于每一个新的表面.

  • session.surfaces 按创建顺序列出每个活表面;一个表面,代理删除它,一个再创建移动到结尾。 每个转弯 response.surfaces 列出生成的活表面; 找到一个没有生成的, 如外壳, 由其 rootComponentType。只有在创建它的运行流出时才列出加载占位符:当其转动或初始化流出时,或者当飞行流中的请求进入该运行而未发现其已留下时;留下的代理人(取消运行、出错、恢复会话)之一离开列表,所以消费者为列出的每个表面画出一个骨架 isLoading 别动.
  • session.a2uiMessages 按照到达顺序是整个会话的渲染流。 它只生长,所以一个单一的渲染器每个新消息都会被喂入一次. a 之前,一个 createSurface 代替活表面,它携带一个 deleteSurface 因为它没有在表面做活的手术, 因为造物者拒绝两者。 它之所以存在,是因为Agent Gateways exits A2-UI v1.0,而每个可用的渲染器消耗v0.9:热电压降低消费者代表的流(已记录的临时债务,见下文).
  • session.state 是每个活地表的验证数据模型,由地表id键入,以所写的运行为准,上面有同时动作的答案.
  • 一个动作解决了整个会话的活表面的源组件,所以更早运行的表面会不断接受动作. 它的运行只更新了表面:它的表面快照用活转来记录,或者在一转前进行初始化,它永远不会改变一转或初始化的状态或反应. 当它失败,不管终点拒绝它,它会流出 RUN_ERROR,其流结束没有终端事件或请求无法构建, session.actionError 识别此动作( e)message, (中文(简体) ), component, (中文(简体) ), name, (中文(简体) ), surfaceId, (中文(简体) ), sourceComponentId),因此消费者可以显示它来自的街区旁边的故障.

每个 DiscoveredSurface 携带 surfaceId, (中文(简体) ), rootComponentType,以及 isLoading,这是 true 当表层的数据模型宣布时 isLoading: true (用于 rootComponentType) (中文(简体) ).

验证和显示值

  • 客户端验证保留,透明. 注射时 contracts schema仍然是唯一的验证来源,但消费者从未运行Zod并写出任何适配器胶. 入境 updateDataModel 行动在应用到 session.state,与每个活地表面的组件相对抗;出站动作有效载荷在POST之前的私人发送路径被验证. 两种边界都生活在核心内. session.a2uiMessages 来源于生源流,所以渲染器仍然收到一个操作,验证从 session.state,且组件状态由 createSurface.dataModel,它永远不会被验证.
  • 显示值格式是代理生成的. 渲染器显示它通过其 { path } 绑定; Thermidor 不重塑显示的值.

注射计划、终点和上下文

  • 合同计划a 由消费者注入的Zod受歧视工会contracts)是组件状态和动作负载验证的唯一来源。 通过结构打字接缝(ContractsSchema 编号 : ComponentContractSchema 编号 : ParsableSchema)只读出公共 Zod API,因此它宣布不依赖任何特定的计划包. 它宣布 zod 作为跨两大类的同伴依赖关系3.25.76 || ^4),所以验证通过单一共享 zod 实例通过注入的图解。 由Zod 3制造出制造器的消费者注入了Zod 3图案,例如: @coveo/thermidor-schema/zod3。 。 。 .
  • 终点 何时 endpoint 提供客户邮递服务。 如果缺席,就解决了 https://{orgId}.org.coveo.com 并附加固定的逆向路径 /api/preview/organizations/{orgId}/agents/commerce/agui/converse。 。 。 .
  • 上下文提供者 —— 说 navigatorContextProvider (所需)和 commerceContextProvider 是每个请求读取的同步函数。 导航器上下文包含请求的 language, (中文(简体) ), country 财务报告和审定财务报表 currency。上下文从不存储在会话上,也从不序列化.

序列化

session.serialize() 生成一个版本 SerializedSession (整数) version >= 1),不同于运行时间 Turn 类型。 传回去吧 sessionToRestore 内 createSession 继续谈话 开始,转弯和会话记录 state 表面和成形器流从未存在过 activities 在恢复时); 转弯或初始化在中游持续; 降级为 error 与 'Stream was interrupted'保留它持有的东西.

记录的临时债务和范围外的项目

以下是故意的 尚未执行 并记录为债务或延期工作:

  • 临时地表引出 —— 说 surfaces 是来自 activities,注释为临时;核心解释为否 rootComponentType;消费者阅读. 服务器发送打入的路由后, 它将被退役 .
  • A2-UI v0.9 渲染器投影 —— 说 session.a2uiMessages 降级代理Gateway的v1.0流为今天的v0.9渲染器. 一旦 V1.0 有能力的渲染器存在,则在断裂变化中删除 .
  • 渲染器流绕过验证 - 输入操作,即中途验证从 session.state 仍然通过 session.a2uiMessages,组件的种子由 createSurface.dataModel,它永远不会被验证。 固定必须覆盖两条路.
  • 在渲染器边界上强制操作负载打字 - 变形手 dispatchAction 输入的动作信息很松散,因此,每个组件的动作打字由公约适用(消费者选择输入生成的 XxxAction 而不是在边界上执行。 如果软件包拥有约束性分辨率,则以后可以恢复.
  • 软件包重命名 ——被推迟为低价;一揽子措施仍然有效 @coveo/thermidor。 。 。 .

发展

pnpm --filter @coveo/thermidor build      # build project
pnpm --filter @coveo/thermidor test       # run unit + property tests
pnpm --filter @coveo/thermidor test:dts   # validate public API surface (incl. DX type test)

建筑

见 架构指南 内部结构,以及 docs/internal/adr。 。 。 .

版本与发布记录

@coveo/thermidor 共发布 15 个版本,首次发布于 2026/06/25;过去 12 个月发布了 15 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.0latest2026/11/113 天前
0.8.12026/11/094 天前
0.8.02026/11/0410 天前
0.7.02026/11/0311 天前
0.6.02026/10/2717 天前
0.5.02026/10/2025 天前

关于 @coveo/thermidor 的常见问题

@coveo/thermidor 是什么?

用于搜索和谈话体验的统一、框架不可知头引擎的实验软件包

如何安装 @coveo/thermidor?

在项目目录运行 npm install @coveo/thermidor,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:zod(3.25.76 || ^4)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@coveo/thermidor 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@coveo/thermidor 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@coveo/thermidor 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js ^22.11.0 || ^24.11.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@coveo/thermidor 可以商用吗?

@coveo/thermidor 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@coveo/thermidor 的下载量有多少?

根据 npm 官方下载统计,@coveo/thermidor 在 2026/10/11 – 2026/11/09 这 30 天内被下载 2,116 次,日均约 71 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@coveo/thermidor 还在维护吗?

最新版本 0.9.0 发布于 2026/11/11(3 天前);至今共发布 15 个版本,其中过去 12 个月发布了 15 个;首次发布于 2026/06/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。