109 lines
3.0 KiB
JavaScript
109 lines
3.0 KiB
JavaScript
/** 将会话消息导出为可读 Markdown 文本 */
|
|
|
|
function formatExportTime(iso) {
|
|
if (!iso) return "";
|
|
const d = new Date(iso);
|
|
return d.toLocaleString("zh-CN", {
|
|
month: "numeric",
|
|
day: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
});
|
|
}
|
|
|
|
const KIND_LABELS = {
|
|
user_input: "用户输入",
|
|
orchestrator_decision: "编排器决策",
|
|
orchestrator_prompt: "编排器询问",
|
|
agent_tool: "工具",
|
|
worker_running: "执行单元 · 执行",
|
|
worker_output: "执行单元 · 产出",
|
|
worker_stub: "执行单元 · 占位",
|
|
system_info: "系统",
|
|
error: "错误",
|
|
};
|
|
|
|
export function sessionToMarkdown(view) {
|
|
if (!view) return "";
|
|
|
|
const lines = [];
|
|
lines.push(`# ${view.bookTitle ?? "未命名作品"}`);
|
|
lines.push("");
|
|
lines.push(`- Skill: \`${view.activeSkill ?? "—"}\``);
|
|
lines.push(`- 阶段: ${view.phase ?? "—"}`);
|
|
if (view.waitingReason?.kind) {
|
|
lines.push(`- 等待: ${view.waitingReason.kind}`);
|
|
}
|
|
lines.push(`- 导出时间: ${new Date().toLocaleString("zh-CN")}`);
|
|
lines.push("");
|
|
|
|
if (view.skillCatalog?.length) {
|
|
const stageLabel = view.lifecycleStage === "play" ? "游玩" : "创作";
|
|
lines.push(`## ${stageLabel}技能清单`);
|
|
lines.push("");
|
|
for (const skill of view.skillCatalog) {
|
|
const mark =
|
|
skill.status === "done"
|
|
? "x"
|
|
: skill.status === "active"
|
|
? ">"
|
|
: " ";
|
|
lines.push(`- [${mark}] **${skill.label}** (\`${skill.id}\`) — ${skill.purpose}`);
|
|
}
|
|
lines.push("");
|
|
} else if (view.pipeline?.length) {
|
|
lines.push("## 流程进度");
|
|
lines.push("");
|
|
for (const step of view.pipeline) {
|
|
const mark =
|
|
step.status === "done" ? "x" : step.status === "active" ? ">" : " ";
|
|
lines.push(`- [${mark}] ${step.label}`);
|
|
}
|
|
lines.push("");
|
|
}
|
|
|
|
lines.push("## 对话与调度记录");
|
|
lines.push("");
|
|
|
|
for (const msg of view.messages ?? []) {
|
|
const kind = msg.kind ?? (msg.role === "user" ? "user_input" : "system_info");
|
|
const label = msg.title ?? KIND_LABELS[kind] ?? kind;
|
|
lines.push(`### ${label}`);
|
|
lines.push("");
|
|
lines.push(`*${formatExportTime(msg.createdAt)}${msg.actor ? ` · ${msg.actor}` : ""}*`);
|
|
|
|
if (msg.tokenUsage?.totalTokens) {
|
|
lines.push(`*Token: ${msg.tokenUsage.totalTokens}*`);
|
|
}
|
|
lines.push("");
|
|
|
|
if (msg.thinking?.trim()) {
|
|
lines.push("**思维链**");
|
|
lines.push("");
|
|
lines.push(msg.thinking.trim());
|
|
lines.push("");
|
|
}
|
|
|
|
const body = (msg.body ?? msg.text ?? "").trim();
|
|
if (body) {
|
|
lines.push(body);
|
|
lines.push("");
|
|
}
|
|
|
|
lines.push("---");
|
|
lines.push("");
|
|
}
|
|
|
|
return lines.join("\n").trim() + "\n";
|
|
}
|
|
|
|
export function downloadMarkdown(filename, content) {
|
|
const blob = new Blob([content], { type: "text/markdown;charset=utf-8" });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = filename;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|