Files
PlantAI-rag-new/ui/src/api/docgen.js
T
adminandClaude Fable 5 671783eb1e fix(ui): r3 前端 10 项缺陷修复(#7/#27/#28/#31/#39/#40/#42/#47b/#52/#55)
- #7a reveal 超时:长文按总时长 ≤8s 反推 interval(下限 12ms),短路径
  30ms/字符不变(step=1);跳过按钮与 reduced-motion 行为不变
- #7b 标题编号:heading_open 注入 h1「一、」/h2「1.1」/h3「1.2.3」,
  层级变化归零、自带编号跳过、md-hN class 恒挂、计数器逐次渲染重置;
  DocPane md 回退预览与私有参考块用 renderMarkdownPlain 关闭注入
  (防止编号被序列化写回正本)
- #27 会话聚合:删除前端二次聚合,会话域直接以后端 sources 为准;
  SourcesPane/SourceRow 以 paper_id/id 为 key 与高亮依据,徽标显示
  真实 paper_id(截断+悬浮),无 paper_id 退 index+1;
  setHighlightSource 参数 index→key,chip 编号契约保持
- #31 节点详情:KgEntityDetail fetchExtra 序号守卫;KgPane :key 强制
  重建;KgGraphView pointer 事件重构(pointerdown 不捕获、位移>4px
  才捕获+标记 dragging、pointerup release、onClickNode dragging 拦截)
- #39 日志刷新:打开总是重拉+「刷新日志」按钮+有 running 时 5s 独立
  轮询(onBeforeUnmount 清理)
- #40 轮询暂停:任何离开 extract 的切换 pausePolling;详情/日志展开态
  提升到 store(detailOpenId/logOpen/runLog/logError),切 TAB 保留
- #42 模板选择:Composer docgen 模式模板下拉(自动推荐+内置+as_template
  懒加载,失败降级),template_id 经 sendDocgen 传入
- #47b 会话切换 resetSession(selectSession 动态 import,只清不丢)
- #55 文档 TAB:docgen store viewMode + DocPane「资料库」/「关闭文档」/
  「返回文档」按钮
- #28 检索文案:searchNoAnswer/searchTimeoutEmpty;200 降级体按
  payload.degraded 分流,不再走 _errorMarker TIMEOUT 报错
- #52 异步轮询:api getTask/listTasks;sendDocgen 兼容 200/202,
  202 后释放 busy 锁(生成期间可继续对话)、消息块记录 task_id、
  2s 轮询 getTask、progress 驱动「生成中 x/y 节」、done 复用现有
  artifact 挂块+落库+adoptArtifact+切 TAB、error 可重试;
  会话恢复时 running 块含 task_id 续轮询(不误标「已中断」)

自测:29 个新增 r3debug 单测 + 23 个既有 private_docs 测试全绿;
npm run build 通过;playwright 冒烟(dev server 5199)无 JS 错误,
模板下拉/提取面板/日志刷新按钮渲染正常。
未 push;Debug-2 9090 未就绪,#52/#28 联调留待 9090 就绪后执行。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-10 16:17:11 +08:00

91 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// api/docgen.js —— 文档生成端点(P3/dev3 归属)
// 端点契约:P2 任务书 sdk/app/routers/docgen.py(dev2 开发中,联调后生效)。
// 全部经 http.js 封装;默认读超时 600s 兜底(生成/编辑最长路径不早于 BFF 超时自断)。
import { get, post, patch, del } from './http.js';
// 缺陷 #52:generate 兼容 200(旧同步契约)/202(新异步契约)两种响应形态。
// 202 由 http.js 返回 payload(202 在 fetch 语义下非错误,body 直接透传);
// 旧 BFF 无 202 时直接返回同步产物——两者统一经 generate() 归一,
// chat.sendDocgen 按 `payload.task_id` 有无区分走轮询还是直接落块。
/**
* 上传入库(用户拖拽即走此路):multipart → vstore /ingest,入库主向量库持久可用。
* @param {File} file
* @param {{ name?: string, category?: string, description?: string, tags?: string, as_template?: boolean }} meta
* @param {{ onUploadProgress?: (e: {loaded:number, total:number, pct:number}) => void }} [opts]
* onUploadProgress:真实上传字节进度(http.js XHR 分支回调)
* @returns {Promise<{ok:boolean, items:[{library_id, name, size, as_template}]}>}
*/
export function uploadLibrary(file, meta = {}, opts = {}) {
const form = new FormData();
form.append('file', file);
if (meta.name != null && meta.name !== '') form.append('name', meta.name);
if (meta.category != null && meta.category !== '') form.append('category', meta.category);
if (meta.description != null && meta.description !== '') form.append('description', meta.description);
if (meta.tags != null && meta.tags !== '') form.append('tags', meta.tags);
if (meta.as_template != null) form.append('as_template', meta.as_template ? 'true' : 'false');
return post('/api/docgen/library', form, opts);
}
/**
* 生成(600s 兜底读超时;兼容 200 同步 / 202 异步两种响应)。
* @returns {Promise<{ok, task_id?, status?, artifact?, progress?, markdown?}>}
* 202 → {ok, task_id, status:"queued"};200 → 旧同步产物(artifact 内嵌)。
*/
export function generate(payload) {
return post('/api/docgen/generate', payload);
}
/** 缺陷 #52:任务状态轮询(GET /api/docgen/tasks/{task_id})
* → 200 {ok, task:{id, session_id, status, progress:[{section,title,state}], artifact|null, error}} */
export function getTask(taskId) {
return get(`/api/docgen/tasks/${encodeURIComponent(taskId)}`);
}
/** 缺陷 #52:会话级任务列表(GET /api/docgen/tasks?session_id=)——会话恢复时兜底查询 */
export function listTasks(sessionId) {
return get('/api/docgen/tasks', { session_id: sessionId });
}
/** 取 artifact 的 md 原文 */
export function getArtifactContent(artifactId) {
return get(`/api/docgen/artifacts/${encodeURIComponent(artifactId)}/content`);
}
/** 取 artifact 的模版 HTML({html, css, template_key},预览与导出同源) */
export function getArtifactHtml(artifactId) {
return get(`/api/docgen/artifacts/${encodeURIComponent(artifactId)}/html`);
}
/** 保存编辑后的 markdown(debounce PATCH);letterhead 为红头文字(2026-09-04 就地编辑持久化) */
export function saveArtifactContent(artifactId, markdown, letterhead) {
return patch(`/api/docgen/artifacts/${encodeURIComponent(artifactId)}/content`, { markdown, letterhead });
}
/** 「用 prompt 修改」:后端按 instruction 返回完整新 md */
export function editArtifact(artifactId, instruction) {
return post(`/api/docgen/artifacts/${encodeURIComponent(artifactId)}/edit`, { instruction });
}
/** 导出:后端生成产物并返回 download_url(PDF/DOCX) */
export function exportArtifact(artifactId, format) {
return post(`/api/docgen/artifacts/${encodeURIComponent(artifactId)}/export`, { format });
}
/** 主库管理(透传 vstore;批量导入/模版浏览用) */
export function listLibrary({ q, category, as_template } = {}) {
return get('/api/docgen/library', { q, category, as_template });
}
/** 删除资料(FR-PRIV-006):BFF DELETE 返回 204 空体——http.del 解析不出 JSON 时
* 返回 null,此处兜底为 {ok:true}(假设 10:不抛 JSON 解析错);信封错误仍抛 ApiError。 */
export async function removeLibrary(id) {
const res = await del(`/api/docgen/library/${encodeURIComponent(id)}`);
return res == null ? { ok: true } : res;
}
/** 下载地址(附件导出:后端带 RFC 5987 文件名) */
export function downloadUrl(artifactId, format) {
return `/api/docgen/artifacts/${encodeURIComponent(artifactId)}/download?format=${encodeURIComponent(format)}`;
}