feat(chat): stream and persist local answer UI with final report references

This commit is contained in:
2026-10-12 18:54:18 +08:00
parent a31b76a793
commit f5c3cde2c2
20 changed files with 827 additions and 105 deletions
+6 -3
View File
@@ -1,7 +1,10 @@
import { tool } from "../tool.js";
export default tool({
description: "提交本轮完整的最终答复。只在全部工具结果、浏览器回执和业务写回已核实后调用;阶段说明留在过程记录。此工具不停止执行,也不代表任务、工单或运行已完成。",
args: { answer: tool.schema.string().min(1).max(30000) },
async execute({ answer }) { return JSON.stringify({ answer }); },
description: "提交本轮完整的最终答复。全部工具结果与需要的业务写回已核实后调用;需要浏览器动作回执时先核实回执,本地回答界面发布不等待浏览器回执;阶段说明留在过程记录。answer 是最后报告的完整文字;ui_block_ids 只列已经用 present_answer 发布且应保留在最终报告中的 block_id,按报告顺序排列。收到最终报告后,其他过程文字、界面与轨迹自动折叠。已发布本地交互界面的聊天会在此工具完成后结束本轮,之后不能继续更新界面;需要补充内容应先更新再提交最终答复。后台任务仍以 Server 的完成/失败接口为准,此工具不代表任务、工单或运行已完成。",
args: {
answer: tool.schema.string().min(1).max(30000),
ui_block_ids: tool.schema.array(tool.schema.string().regex(/^[a-zA-Z0-9_-]{1,80}$/)).max(48).optional(),
},
async execute({ answer, ui_block_ids }) { return JSON.stringify({ answer, ui_block_ids: ui_block_ids ?? [] }); },
});
+18
View File
@@ -0,0 +1,18 @@
import { tool } from "../tool.js";
import { answerUiInputSchema } from "../../src/chat/answer-ui.js";
import catalog from "../../src/chat/answer-ui-catalog.json" with { type: "json" };
export default tool({
description: "把回答的一段内容发布为本地交互界面。spec 是 root + elements 的扁平树,每个元素包括 type、props、children。只组织真实查询资料或用户事实;无脚本、URL 或任意代码。不执行地图或业务动作,不等待浏览器,不代表业务完成。相同 block_id 更新本轮同一内容块。组件参数的 JSON Schema:" + JSON.stringify(catalog.props),
args: {
block_id: tool.schema.string().regex(/^[a-zA-Z0-9_-]{1,80}$/),
fallback_text: tool.schema.string().min(1).max(4000),
spec: tool.schema.object({ root: tool.schema.string(), elements: tool.schema.record(tool.schema.string(), tool.schema.object({
type: tool.schema.string(), props: tool.schema.record(tool.schema.string(), tool.schema.unknown()), children: tool.schema.array(tool.schema.string()).max(48),
}).strict()) }).strict(),
},
async execute(args) {
const value = answerUiInputSchema.parse(args);
return JSON.stringify({ version: "answer-ui@1", ...value, recorded: true });
},
});