@langgraph-js/sdk
Version:
The UI SDK for LangGraph - seamlessly integrate your AI agents with frontend interfaces
99 lines (98 loc) • 3.83 kB
JavaScript
import { atom, effect } from "nanostores";
export * from "./types.js";
import { ToolRenderData } from "../tool/ToolUI.js";
// 创建 artifacts computed store
const extractArtifactsFromMessages = (renderMessages, client) => {
const files = new Map();
for (const message of renderMessages) {
if (message.type === "tool" && message.name === "create_artifacts") {
const tool = new ToolRenderData(message, client);
const command = tool.getInputRepaired();
if (!command.id)
continue;
command.tool_id = tool.message.id;
command.is_done = tool.state === "done";
files.set(command.id, [...(files.get(command.id) || []), command]);
}
}
const composedFiles = new Map();
// 遍历每个 ID 的命令序列,生成对应的 artifact 版本
for (const [id, commands] of files) {
const artifacts = [];
let currentContent = "";
let currentFilename = "";
let currentFiletype = "";
let version = 1;
// 按命令顺序处理每个操作
for (const command of commands) {
switch (command.command) {
case "create":
// 创建新 artifact,直接使用 content
currentContent = command.content;
currentFilename = command.title || `artifact-${id}`;
currentFiletype = command.type || command.language;
break;
case "update":
// 更新现有内容,使用 old_str 和 new_str 进行替换
if (command.old_str && command.new_str) {
currentContent = currentContent.replace(command.old_str, command.new_str);
}
else if (command.content) {
// 如果没有 old_str/new_str,则直接使用 content 覆盖
currentContent = command.content;
}
break;
case "rewrite":
currentContent = command.content;
break;
}
// 创建当前版本的 artifact
const artifact = {
group_id: id,
id: command.tool_id,
code: currentContent,
filename: currentFilename,
filetype: currentFiletype,
version: version,
is_done: command.is_done,
};
artifacts.push(artifact);
version++;
}
composedFiles.set(id, artifacts);
}
return [...composedFiles.values()].map((artifacts) => ({
id: artifacts[0].group_id,
filename: artifacts[artifacts.length - 1].filename,
filetype: artifacts[artifacts.length - 1].filetype,
versions: artifacts,
}));
};
export const useArtifacts = (renderMessages, client) => {
// 创建 artifacts store
const showArtifact = atom(false);
const currentArtifactId = atom(null);
const artifacts = atom([]);
effect([renderMessages, client], () => {
artifacts.set(extractArtifactsFromMessages(renderMessages.get(), client.get()));
});
const debouncedSetCurrentArtifactById = (id, tool_id) => {
const current = currentArtifactId.get();
if (current?.[0] === id && current?.[1] === tool_id) {
return;
}
showArtifact.set(true);
currentArtifactId.set([id, tool_id]);
};
return {
data: {
artifacts,
currentArtifactId,
showArtifact,
},
mutation: {
setCurrentArtifactById: debouncedSetCurrentArtifactById,
setShowArtifact: (show) => showArtifact.set(show),
},
};
};